/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.Writer; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.common.AtomicFileWritingIdiom.OutputStreamStatement; import org.apache.zookeeper.common.AtomicFileWritingIdiom.WriterStatement; import org.junit.BeforeClass; import org.junit.Test; public class AtomicFileWritingIdiomTest extends ZKTestCase { private static File tmpdir; @BeforeClass public static void createTmpDir() { tmpdir = new File("build/test/tmp"); tmpdir.mkdirs(); } @Test public void testOutputStreamSuccess() throws IOException { File target = new File(tmpdir, "target.txt"); final File tmp = new File(tmpdir, "target.txt.tmp"); createFile(target, "before"); assertEquals("before", getContent(target)); new AtomicFileWritingIdiom(target, new OutputStreamStatement() { @Override public void write(OutputStream os) throws IOException { os.write("after".getBytes("ASCII")); assertTrue("implementation of AtomicFileOutputStream has changed, update the test", tmp.exists()); } }); assertFalse("tmp file should have been deleted", tmp.exists()); // content changed assertEquals("after", getContent(target)); target.delete(); } @Test public void testWriterSuccess() throws IOException { File target = new File(tmpdir, "target.txt"); final File tmp = new File(tmpdir, "target.txt.tmp"); createFile(target, "before"); assertEquals("before", getContent(target)); new AtomicFileWritingIdiom(target, new WriterStatement() { @Override public void write(Writer os) throws IOException { os.write("after"); assertTrue("implementation of AtomicFileOutputStream has changed, update the test", tmp.exists()); } }); assertFalse("tmp file should have been deleted", tmp.exists()); // content changed assertEquals("after", getContent(target)); target.delete(); } @Test public void testOutputStreamFailure() throws IOException { File target = new File(tmpdir, "target.txt"); final File tmp = new File(tmpdir, "target.txt.tmp"); createFile(target, "before"); assertEquals("before", getContent(target)); boolean exception = false; try { new AtomicFileWritingIdiom(target, new OutputStreamStatement() { @Override public void write(OutputStream os) throws IOException { os.write("after".getBytes("ASCII")); os.flush(); assertTrue("implementation of AtomicFileOutputStream has changed, update the test", tmp.exists()); throw new RuntimeException(); } }); } catch (RuntimeException ex) { exception = true; } assertFalse("tmp file should have been deleted", tmp.exists()); assertTrue("should have raised an exception", exception); // content preserved assertEquals("before", getContent(target)); target.delete(); } @Test public void testWriterFailure() throws IOException { File target = new File(tmpdir, "target.txt"); final File tmp = new File(tmpdir, "target.txt.tmp"); createFile(target, "before"); assertEquals("before", getContent(target)); boolean exception = false; try { new AtomicFileWritingIdiom(target, new WriterStatement() { @Override public void write(Writer os) throws IOException { os.write("after"); os.flush(); assertTrue("implementation of AtomicFileOutputStream has changed, update the test", tmp.exists()); throw new RuntimeException(); } }); } catch (RuntimeException ex) { exception = true; } assertFalse("tmp file should have been deleted", tmp.exists()); assertTrue("should have raised an exception", exception); // content preserved assertEquals("before", getContent(target)); target.delete(); } @Test public void testOutputStreamFailureIOException() throws IOException { File target = new File(tmpdir, "target.txt"); final File tmp = new File(tmpdir, "target.txt.tmp"); createFile(target, "before"); assertEquals("before", getContent(target)); boolean exception = false; try { new AtomicFileWritingIdiom(target, new OutputStreamStatement() { @Override public void write(OutputStream os) throws IOException { os.write("after".getBytes("ASCII")); os.flush(); assertTrue("implementation of AtomicFileOutputStream has changed, update the test", tmp.exists()); throw new IOException(); } }); } catch (IOException ex) { exception = true; } assertFalse("tmp file should have been deleted", tmp.exists()); assertTrue("should have raised an exception", exception); // content preserved assertEquals("before", getContent(target)); target.delete(); } @Test public void testWriterFailureIOException() throws IOException { File target = new File(tmpdir, "target.txt"); final File tmp = new File(tmpdir, "target.txt.tmp"); createFile(target, "before"); assertEquals("before", getContent(target)); boolean exception = false; try { new AtomicFileWritingIdiom(target, new WriterStatement() { @Override public void write(Writer os) throws IOException { os.write("after"); os.flush(); assertTrue("implementation of AtomicFileOutputStream has changed, update the test", tmp.exists()); throw new IOException(); } }); } catch (IOException ex) { exception = true; } assertFalse("tmp file should have been deleted", tmp.exists()); assertTrue("should have raised an exception", exception); // content preserved assertEquals("before", getContent(target)); target.delete(); } @Test public void testOutputStreamFailureError() throws IOException { File target = new File(tmpdir, "target.txt"); final File tmp = new File(tmpdir, "target.txt.tmp"); createFile(target, "before"); assertEquals("before", getContent(target)); boolean exception = false; try { new AtomicFileWritingIdiom(target, new OutputStreamStatement() { @Override public void write(OutputStream os) throws IOException { os.write("after".getBytes("ASCII")); os.flush(); assertTrue("implementation of AtomicFileOutputStream has changed, update the test", tmp.exists()); throw new Error(); } }); } catch (Error ex) { exception = true; } assertFalse("tmp file should have been deleted", tmp.exists()); assertTrue("should have raised an exception", exception); // content preserved assertEquals("before", getContent(target)); target.delete(); } @Test public void testWriterFailureError() throws IOException { File target = new File(tmpdir, "target.txt"); final File tmp = new File(tmpdir, "target.txt.tmp"); createFile(target, "before"); assertEquals("before", getContent(target)); boolean exception = false; try { new AtomicFileWritingIdiom(target, new WriterStatement() { @Override public void write(Writer os) throws IOException { os.write("after"); os.flush(); assertTrue("implementation of AtomicFileOutputStream has changed, update the test", tmp.exists()); throw new Error(); } }); } catch (Error ex) { exception = true; } assertFalse("tmp file should have been deleted", tmp.exists()); assertTrue("should have raised an exception", exception); // content preserved assertEquals("before", getContent(target)); target.delete(); } // ************** target file does not exist @Test public void testOutputStreamSuccessNE() throws IOException { File target = new File(tmpdir, "target.txt"); final File tmp = new File(tmpdir, "target.txt.tmp"); target.delete(); assertFalse("file should not exist", target.exists()); new AtomicFileWritingIdiom(target, new OutputStreamStatement() { @Override public void write(OutputStream os) throws IOException { os.write("after".getBytes("ASCII")); assertTrue("implementation of AtomicFileOutputStream has changed, update the test", tmp.exists()); } }); // content changed assertEquals("after", getContent(target)); target.delete(); } @Test public void testWriterSuccessNE() throws IOException { File target = new File(tmpdir, "target.txt"); final File tmp = new File(tmpdir, "target.txt.tmp"); target.delete(); assertFalse("file should not exist", target.exists()); new AtomicFileWritingIdiom(target, new WriterStatement() { @Override public void write(Writer os) throws IOException { os.write("after"); assertTrue("implementation of AtomicFileOutputStream has changed, update the test", tmp.exists()); } }); assertFalse("tmp file should have been deleted", tmp.exists()); // content changed assertEquals("after", getContent(target)); target.delete(); } @Test public void testOutputStreamFailureNE() throws IOException { File target = new File(tmpdir, "target.txt"); final File tmp = new File(tmpdir, "target.txt.tmp"); target.delete(); assertFalse("file should not exist", target.exists()); boolean exception = false; try { new AtomicFileWritingIdiom(target, new OutputStreamStatement() { @Override public void write(OutputStream os) throws IOException { os.write("after".getBytes("ASCII")); os.flush(); assertTrue("implementation of AtomicFileOutputStream has changed, update the test", tmp.exists()); throw new RuntimeException(); } }); } catch (RuntimeException ex) { exception = true; } assertFalse("tmp file should have been deleted", tmp.exists()); assertTrue("should have raised an exception", exception); // file should not exist assertFalse("file should not exist", target.exists()); } @Test public void testWriterFailureNE() throws IOException { File target = new File(tmpdir, "target.txt"); final File tmp = new File(tmpdir, "target.txt.tmp"); target.delete(); assertFalse("file should not exist", target.exists()); boolean exception = false; try { new AtomicFileWritingIdiom(target, new WriterStatement() { @Override public void write(Writer os) throws IOException { os.write("after"); os.flush(); assertTrue("implementation of AtomicFileOutputStream has changed, update the test", tmp.exists()); throw new RuntimeException(); } }); } catch (RuntimeException ex) { exception = true; } assertFalse("tmp file should have been deleted", tmp.exists()); assertTrue("should have raised an exception", exception); // file should not exist assertFalse("file should not exist", target.exists()); } private String getContent(File file, String encoding) throws IOException { StringBuilder result = new StringBuilder(); FileInputStream fis = new FileInputStream(file); byte[] b = new byte[20]; int nb; while ((nb = fis.read(b)) != -1) { result.append(new String(b, 0, nb, encoding)); } fis.close(); return result.toString(); } private String getContent(File file) throws IOException { return getContent(file, "ASCII"); } private void createFile(File file, String content) throws IOException { FileOutputStream fos = new FileOutputStream(file); fos.write(content.getBytes("ASCII")); fos.close(); } }