Ticket #24278: 24278_B.patch
| File 24278_B.patch, 22.8 KB (added by , 9 months ago) |
|---|
-
build.xml
4 4 <!-- enter the SVN commit message --> 5 5 <property name="commit.message" value=""/> 6 6 <!-- enter the *lowest* JOSM version this plugin is currently compatible with --> 7 <property name="plugin.main.version" value="19 044"/>7 <property name="plugin.main.version" value="19389"/> 8 8 9 9 <property name="plugin.author" value="Paul Hartmann"/> 10 10 <property name="plugin.class" value="org.openstreetmap.josm.plugins.photo_geotagging.GeotaggingPlugin"/> -
pom.xml
16 16 </developers> 17 17 <properties> 18 18 <plugin.src.dir>src</plugin.src.dir> 19 <plugin.main.version>19 044</plugin.main.version>19 <plugin.main.version>19389</plugin.main.version> 20 20 <plugin.author>Paul Hartmann</plugin.author> 21 21 <plugin.class>org.openstreetmap.josm.plugins.photo_geotagging.GeotaggingPlugin</plugin.class> 22 22 <plugin.description>Write gps position info to the image file header. Run this feature from the right click menu of the image layer.</plugin.description> -
src/org/openstreetmap/josm/plugins/photo_geotagging/ExifGPSTagger.java
9 9 import java.io.FileOutputStream; 10 10 import java.io.IOException; 11 11 import java.text.DecimalFormat; 12 import java.time.Instant;13 12 import java.util.Calendar; 14 13 import java.util.GregorianCalendar; 15 14 import java.util.TimeZone; … … 25 24 import org.apache.commons.imaging.formats.tiff.write.TiffImageWriterLossy; 26 25 import org.apache.commons.imaging.formats.tiff.write.TiffOutputDirectory; 27 26 import org.apache.commons.imaging.formats.tiff.write.TiffOutputSet; 27 import org.openstreetmap.josm.gui.layer.geoimage.ImageEntry; 28 28 29 29 /** 30 30 * Wrapper class for sanselan library … … 40 40 * 41 41 * @param imageFile A source image file. 42 42 * @param dst The output file. 43 * @param lat latitude 44 * @param lon longitude 45 * @param gpsTime time - can be null if not available 46 * @param speed speed in km/h - can be null if not available 47 * @param ele elevation - can be null if not available 48 * @param imgDir image direction in degrees (0..360) - can be null if not available 43 * @param imageEntry the image object from Josm core 49 44 * @param lossy whether to use lossy approach when writing metadata (overwriting unknown tags) 50 45 * @throws IOException in case of I/O error 46 * @since xxx separate image parameters (lat, lon, gpsTime, speed, ele, imgDir), replaced by the whole ImageEntry object. 51 47 */ 52 public static void setExifGPSTag(File imageFile, File dst, double lat, double lon, Instant gpsTime, Double speed, 53 Double ele, Double imgDir, boolean lossy) throws IOException { 48 49 public static void setExifGPSTag(File imageFile, File dst, ImageEntry imageEntry, 50 boolean lossy) throws IOException { 54 51 try { 55 setExifGPSTagWorker(imageFile, dst, lat, lon, gpsTime, speed, ele, imgDir, lossy);52 setExifGPSTagWorker(imageFile, dst, imageEntry, lossy); 56 53 } catch (ImagingException ire) { 57 54 // This used to be two separate exceptions; ImageReadException and imageWriteException 58 55 throw new IOException(tr("Read/write error: " + ire.getMessage()), ire); … … 59 56 } 60 57 } 61 58 62 public static void setExifGPSTagWorker(File imageFile, File dst, double lat, double lon, Instant gpsTime, Double speed,63 Double ele, Double imgDir,boolean lossy) throws IOException {59 public static void setExifGPSTagWorker(File imageFile, File dst, ImageEntry imageEntry, 60 boolean lossy) throws IOException { 64 61 65 62 TiffOutputSet outputSet = null; 66 63 ImageMetadata metadata = Imaging.getMetadata(imageFile); … … 80 77 81 78 TiffOutputDirectory gpsDirectory = outputSet.getOrCreateGpsDirectory(); 82 79 gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_VERSION_ID); 83 gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_VERSION_ID, (byte) 2, (byte)3, (byte)0, (byte)0);80 gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_VERSION_ID, (byte) 2, (byte) 3, (byte) 0, (byte) 0); 84 81 85 if ( gpsTime!= null) {82 if (imageEntry.getGpsInstant() != null) { 86 83 Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC")); 87 calendar.setTimeInMillis( gpsTime.toEpochMilli());84 calendar.setTimeInMillis(imageEntry.getGpsInstant().toEpochMilli()); 88 85 89 86 final int year = calendar.get(Calendar.YEAR); 90 87 final int month = calendar.get(Calendar.MONTH) + 1; … … 117 114 gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_DATE_STAMP, dateStamp); 118 115 } 119 116 120 outputSet.setGpsInDegrees( lon, lat);117 outputSet.setGpsInDegrees(imageEntry.getPos().lon(), imageEntry.getPos().lat()); 121 118 122 if ( speed!= null) {119 if (imageEntry.getSpeed() != null) { 123 120 gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_SPEED_REF); 124 121 gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_SPEED_REF, 125 122 GpsTagConstants.GPS_TAG_GPS_SPEED_REF_VALUE_KMPH); 126 123 127 124 gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_SPEED); 128 gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_SPEED, RationalNumber.valueOf( speed));125 gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_SPEED, RationalNumber.valueOf(imageEntry.getSpeed())); 129 126 } 130 127 131 if ( ele!= null) {132 byte eleRef = ele>= 0 ? (byte) 0 : (byte) 1;128 if (imageEntry.getElevation() != null) { 129 byte eleRef = imageEntry.getElevation() >= 0 ? (byte) 0 : (byte) 1; 133 130 gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_ALTITUDE_REF); 134 131 gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_ALTITUDE_REF, eleRef); 135 132 136 133 gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_ALTITUDE); 137 gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_ALTITUDE, RationalNumber.valueOf(Math.abs( ele)));134 gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_ALTITUDE, RationalNumber.valueOf(Math.abs(imageEntry.getElevation()))); 138 135 } 139 136 140 if (im gDir!= null) {137 if (imageEntry.getExifImgDir() != null) { 141 138 gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION_REF); 142 139 gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION_REF, 143 140 GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION_REF_VALUE_TRUE_NORTH); 144 // make sure the value is in the range 0.0...<360.0 145 if (imgDir < 0.0) { 146 imgDir %= 360.0; // >-360.0...-0.0 147 imgDir += 360.0; // >0.0...360.0 148 } 149 if (imgDir >= 360.0) { 150 imgDir %= 360.0; 151 } 141 Double imgDir = checkAngle(imageEntry.getExifImgDir()); 152 142 gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION); 153 143 gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION, RationalNumber.valueOf(imgDir)); 154 144 } 155 145 146 if (imageEntry.getExifGpsTrack() != null) { 147 gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_TRACK_REF); 148 gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_TRACK_REF, 149 GpsTagConstants.GPS_TAG_GPS_TRACK_REF_VALUE_TRUE_NORTH); 150 // make sure the value is in the range 0.0...<360.0 151 Double gpsTrack = checkAngle(imageEntry.getExifGpsTrack()); 152 gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_TRACK); 153 gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_TRACK, RationalNumber.valueOf(gpsTrack)); 154 } 155 156 if (imageEntry.getGpsDiffMode() != null) { 157 gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_DIFFERENTIAL); 158 // make sure the gpsDiffMode value is 0 (no diffential) or 1 (differential) 159 if (imageEntry.getGpsDiffMode().equals(0) || imageEntry.getGpsDiffMode().equals(1)) { 160 gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_DIFFERENTIAL, imageEntry.getGpsDiffMode().shortValue()); 161 } 162 } 163 164 if (imageEntry.getGps2d3dMode() != null) { 165 gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_MEASURE_MODE); 166 gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_MEASURE_MODE, imageEntry.getGps2d3dMode().toString()); 167 } 168 169 if (imageEntry.getExifGpsProcMethod() != null) { 170 gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_PROCESSING_METHOD); 171 gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_PROCESSING_METHOD, imageEntry.getExifGpsProcMethod()); 172 } 173 174 if (imageEntry.getExifGpsDatum() != null) { 175 gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_MAP_DATUM); 176 gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_MAP_DATUM, imageEntry.getExifGpsDatum().toString()); 177 } 178 179 if (imageEntry.getExifHPosErr() != null) { 180 gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_HOR_POSITIONING_ERROR); 181 gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_HOR_POSITIONING_ERROR, RationalNumber.valueOf(imageEntry.getExifHPosErr())); 182 } 183 184 if (imageEntry.getExifGpsDop() != null) { 185 gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_DOP); 186 gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_DOP, RationalNumber.valueOf(imageEntry.getExifGpsDop())); 187 } 188 156 189 try (BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(dst))) { 157 190 if (metadata instanceof JpegImageMetadata) { 158 191 if (lossy) { … … 165 198 } 166 199 } 167 200 } 201 202 /** 203 * Normalizes an angle to the range [0.0, 360.0[ degrees. 204 * This will fix any angle value <0 and >= 360 205 * @param angle the angle to normalize (in degrees) 206 * @return the equivalent angle value in the range [0.0, 360.0[ 207 */ 208 private static Double checkAngle(Double angle) { 209 if (angle < 0.0) { 210 angle %= 360.0; // >-360.0...-0.0 211 angle += 360.0; // >0.0...360.0 212 } 213 if (angle >= 360.0) { 214 angle %= 360.0; 215 } 216 return angle; 217 } 168 218 } -
src/org/openstreetmap/josm/plugins/photo_geotagging/GeotaggingAction.java
85 85 DecimalFormat dFormatter = new DecimalFormat("###0.000000"); 86 86 87 87 for (ImageEntry e : layer.getImages()) { 88 /* Only write lat/lon to the file, if the position is known and88 /* Only write lat/lon to the file, if the position is known and 89 89 the GPS data changed. */ 90 90 if (e.getPos() != null && e.hasNewGpsData()) { 91 91 String pth = e.getFile().getAbsolutePath(); … … 134 134 135 135 final JPanel settingsPanel = new JPanel(new GridBagLayout()); 136 136 settingsPanel.setBorder(BorderFactory.createTitledBorder(tr("settings"))); 137 cont.add(settingsPanel, GBC.eol().insets(3, 10,3,0));137 cont.add(settingsPanel, GBC.eol().insets(3, 10, 3, 0)); 138 138 139 139 final JCheckBox backups = new JCheckBox(tr("keep backup files"), Config.getPref().getBoolean(KEEP_BACKUP, true)); 140 settingsPanel.add(backups, GBC.eol().insets(3, 3,0,0));140 settingsPanel.add(backups, GBC.eol().insets(3, 3, 0, 0)); 141 141 142 142 final JCheckBox setMTime = new JCheckBox(tr("change file modification time:"), Config.getPref().getBoolean(CHANGE_MTIME, false)); 143 settingsPanel.add(setMTime, GBC.std().insets(3, 3,5,3));143 settingsPanel.add(setMTime, GBC.std().insets(3, 3, 5, 3)); 144 144 145 145 final String[] mTimeModeArray = {"----", tr("to gps time"), tr("to previous value (unchanged mtime)")}; 146 146 final JComboBox<String> mTimeMode = new JComboBox<>(mTimeModeArray); … … 154 154 } 155 155 mTimeMode.setSelectedIndex(setMTime.isSelected() ? mTimeIdx : 0); 156 156 } 157 settingsPanel.add(mTimeMode, GBC.eol().insets(3, 3,3,3));157 settingsPanel.add(mTimeMode, GBC.eol().insets(3, 3, 3, 3)); 158 158 159 159 setMTime.addActionListener(e -> { 160 160 if (setMTime.isSelected()) { … … 225 225 this.mTimeMode = mTimeMode; 226 226 } 227 227 228 229 228 @Override 230 229 protected void realRun() { 231 230 List<ImageEntry> failedEntries = processEntries(images, false); … … 359 358 mTime = e.getExifGpsInstant(); 360 359 } 361 360 } 362 if ( mTimeMode == MTIME_MODE_PREVIOUS_VALUE361 if (mTimeMode == MTIME_MODE_PREVIOUS_VALUE 363 362 // this is also the fallback if one of the other 364 363 // modes failed to determine the modification time 365 364 || (mTimeMode != 0 && mTime == null)) { … … 370 369 371 370 chooseFiles(e.getFile()); 372 371 if (canceled) return; 373 ExifGPSTagger.setExifGPSTag(fileFrom, fileTo, e.getPos().lat(), e.getPos().lon(), 374 e.getGpsInstant(), e.getSpeed(), e.getElevation(), e.getExifImgDir(), lossy); 372 ExifGPSTagger.setExifGPSTag(fileFrom, fileTo, e, lossy); 375 373 376 374 if (mTime != null) { 377 375 if (!fileTo.setLastModified(mTime.toEpochMilli())) … … 390 388 return; 391 389 } 392 390 393 File fileBackup = new File(file.getParentFile(), file.getName()+"_");391 File fileBackup = new File(file.getParentFile(), file.getName()+"_"); 394 392 if (fileBackup.exists()) { 395 393 confirm_override(); 396 394 if (canceled) … … 510 508 } 511 509 512 510 private GeoImageLayer getLayer() { 513 return (GeoImageLayer) LayerListDialog.getInstance().getModel().getSelectedLayers().get(0);511 return (GeoImageLayer) LayerListDialog.getInstance().getModel().getSelectedLayers().get(0); 514 512 } 515 513 516 514 /** … … 538 536 return layers.size() == 1 && layers.get(0) instanceof GeoImageLayer; 539 537 } 540 538 } 539 -
test/unit/org/openstreetmap/josm/plugins/photo_geotagging/ExifGPSTaggerTest.java
18 18 import org.junit.jupiter.api.Test; 19 19 import org.junit.jupiter.api.io.TempDir; 20 20 import org.openstreetmap.josm.TestUtils; 21 import org.openstreetmap.josm.data.coor.LatLon; 22 import org.openstreetmap.josm.gui.layer.geoimage.ImageEntry; 21 23 22 24 class ExifGPSTaggerTest { 23 25 … … 24 26 @TempDir 25 27 File tempFolder; 26 28 29 private static ImageEntry newImageEntry(String file, Double lat, Double lon, Instant exifTime, 30 Double speed, Double elevation, Double imgDir) { 31 ImageEntry entry = new ImageEntry(new File(file)); 32 entry.setPos(new LatLon(lat, lon)); 33 entry.setExifTime(exifTime); 34 entry.setSpeed(speed); 35 entry.setElevation(elevation); 36 entry.setExifImgDir(imgDir); 37 return entry; 38 } 39 27 40 @Test 28 41 void testTicket11757() { 29 42 final File in = new File(TestUtils.getTestDataRoot(), "_DSC1234.jpg"); 30 43 final File out = new File(tempFolder, in.getName()); 31 assertDoesNotThrow(() -> ExifGPSTagger.setExifGPSTag(in, out, 12, 34, Instant.now(), 12.34, Math.E, Math.PI, true)); 44 final ImageEntry image = newImageEntry("test", 12d, 34d, Instant.now(), 12.34d, Math.E, Math.PI); 45 assertDoesNotThrow(() -> ExifGPSTagger.setExifGPSTag(in, out, image, true)); 32 46 } 33 47 34 48 @Test … … 46 60 void testTicket11902() throws Exception { 47 61 final File in = new File(TestUtils.getTestDataRoot(), "IMG_7250_small.JPG"); 48 62 final File out = new File(tempFolder, in.getName()); 49 ExifGPSTagger.setExifGPSTag(in, out, 12, 34, Instant.now(), 12.34, Math.E, Math.PI, false); 63 final ImageEntry image = newImageEntry("test", 12d, 34d, Instant.now(), 12.34d, Math.E, Math.PI); 64 ExifGPSTagger.setExifGPSTag(in, out, image, true); 50 65 final Process jhead = Runtime.getRuntime().exec(new String[]{"jhead", out.getAbsolutePath()}); 51 66 final String stdout = new Scanner(jhead.getErrorStream()).useDelimiter("\\A").next(); 52 67 System.out.println(stdout); 53 68 assertFalse(stdout.contains("Suspicious offset of first Exif IFD value")); 54 69 } 70 71 @Test 72 public void testTicket24278() { 73 final File in = new File(TestUtils.getTestDataRoot(), "_DSC1234.jpg"); 74 final File out = new File(tempFolder, in.getName()); 75 final ImageEntry image = newImageEntry("test", 12d, 34d, Instant.now(), 12.34d, Math.E, Math.PI); 76 image.setExifGpsTrack(Math.PI); 77 image.setGpsDiffMode(2); 78 image.setGps2d3dMode(3); 79 image.setExifGpsProcMethod("GPS"); 80 image.setExifHPosErr(1.2d); 81 image.setExifGpsDop(2.5d); 82 image.setExifGpsDatum("WGS84"); 83 assertDoesNotThrow(() -> ExifGPSTagger.setExifGPSTag(in, out, image, true)); 84 /* TODO read temp file and assertEquals EXIF metadata values */ 85 } 55 86 } -
test/unit/org/openstreetmap/josm/plugins/photo_geotagging/GeotaggingActionTest.java
1 package org.openstreetmap.josm.plugins.photo_geotagging; 2 3 4 import java.io.File; 5 import java.io.IOException; 6 import java.nio.file.Files; 7 import java.nio.file.StandardCopyOption; 8 import java.util.Arrays; 9 import java.util.List; 10 11 import org.junit.jupiter.api.Test; 12 import org.openstreetmap.josm.TestUtils; 13 import org.openstreetmap.josm.data.coor.LatLon; 14 import org.openstreetmap.josm.gui.layer.geoimage.ImageEntry; 15 import org.openstreetmap.josm.plugins.photo_geotagging.GeotaggingAction.GeoTaggingRunnable; 16 17 import static org.junit.jupiter.api.Assertions.assertEquals; 18 import static org.junit.jupiter.api.Assertions.assertTrue; 19 20 class GeotaggingActionTest { 21 22 @Test 23 void testProcessEntries() throws IOException { 24 File original = new File(TestUtils.getTestDataRoot(), "_DSC1234.jpg"); 25 assertTrue(original.exists()); 26 27 File copy = new File(TestUtils.getTestDataRoot(), "_DSC1234.copy.jpg"); 28 if (copy.exists()) copy.delete(); 29 //this method will actually override the file, so use a copy: 30 Files.copy(original.toPath(), copy.toPath(), StandardCopyOption.REPLACE_EXISTING); 31 32 ImageEntry entry = new ImageEntry(copy); 33 entry.setPos(new LatLon(1, 2)); 34 List<ImageEntry> list = Arrays.asList(entry); 35 36 GeoTaggingRunnable runnable = new GeotaggingAction.GeoTaggingRunnable(list, true, 0); 37 //this causes some warnings from the PleaseWaitRunnable because not all resources are available 38 //but that's irrelevant to the test 39 runnable.getProgressMonitor().beginTask("test"); 40 runnable.override_backup = true; 41 assertEquals(1, runnable.processEntries(list, false).size()); 42 assertEquals(0, runnable.processEntries(list, true).size()); 43 //test if overriding backup works: 44 assertEquals(0, runnable.processEntries(list, true).size()); 45 46 runnable = new GeotaggingAction.GeoTaggingRunnable(list, false, 0); 47 runnable.getProgressMonitor().beginTask("test"); 48 //file is now "repaired" from operation above and lossless writing should work: 49 assertEquals(0, runnable.processEntries(list, false).size()); 50 assertEquals(0, runnable.processEntries(list, true).size()); 51 52 File backup = new File(TestUtils.getTestDataRoot(), "_DSC1234.copy.jpg_"); 53 assertTrue(backup.exists()); 54 backup.delete(); 55 copy.delete(); 56 } 57 } 1 package org.openstreetmap.josm.plugins.photo_geotagging; 2 3 4 import java.io.File; 5 import java.io.IOException; 6 import java.nio.file.Files; 7 import java.nio.file.StandardCopyOption; 8 import java.util.Arrays; 9 import java.util.List; 10 11 import org.junit.jupiter.api.Test; 12 import org.openstreetmap.josm.TestUtils; 13 import org.openstreetmap.josm.data.coor.LatLon; 14 import org.openstreetmap.josm.gui.layer.geoimage.ImageEntry; 15 import org.openstreetmap.josm.plugins.photo_geotagging.GeotaggingAction.GeoTaggingRunnable; 16 17 import static org.junit.jupiter.api.Assertions.assertEquals; 18 import static org.junit.jupiter.api.Assertions.assertTrue; 19 20 class GeotaggingActionTest { 21 22 @Test 23 void testProcessEntries() throws IOException { 24 File original = new File(TestUtils.getTestDataRoot(), "_DSC1234.jpg"); 25 assertTrue(original.exists()); 26 27 File copy = new File(TestUtils.getTestDataRoot(), "_DSC1234.copy.jpg"); 28 if (copy.exists()) copy.delete(); 29 //this method will actually override the file, so use a copy: 30 Files.copy(original.toPath(), copy.toPath(), StandardCopyOption.REPLACE_EXISTING); 31 32 ImageEntry entry = new ImageEntry(copy); 33 entry.setPos(new LatLon(1, 2)); 34 List<ImageEntry> list = Arrays.asList(entry); 35 36 GeoTaggingRunnable runnable = new GeotaggingAction.GeoTaggingRunnable(list, true, 0); 37 //this causes some warnings from the PleaseWaitRunnable because not all resources are available 38 //but that's irrelevant to the test 39 runnable.getProgressMonitor().beginTask("test"); 40 runnable.override_backup = true; 41 assertEquals(1, runnable.processEntries(list, false).size()); 42 assertEquals(0, runnable.processEntries(list, true).size()); 43 //test if overriding backup works: 44 assertEquals(0, runnable.processEntries(list, true).size()); 45 46 runnable = new GeotaggingAction.GeoTaggingRunnable(list, false, 0); 47 runnable.getProgressMonitor().beginTask("test"); 48 //file is now "repaired" from operation above and lossless writing should work: 49 assertEquals(0, runnable.processEntries(list, false).size()); 50 assertEquals(0, runnable.processEntries(list, true).size()); 51 52 File backup = new File(TestUtils.getTestDataRoot(), "_DSC1234.copy.jpg_"); 53 assertTrue(backup.exists()); 54 backup.delete(); 55 copy.delete(); 56 } 57 }
