diff --git a/api/net/sf/briar/api/db/ContactId.java b/api/net/sf/briar/api/db/ContactId.java index 3e793c4bfe5e1d1ee74c37a3657b6d3c8e7a5fb2..39d362a3a97eb2f843be2aac283fadc87638e3b3 100644 --- a/api/net/sf/briar/api/db/ContactId.java +++ b/api/net/sf/briar/api/db/ContactId.java @@ -1,6 +1,6 @@ package net.sf.briar.api.db; -/** Uniquely identifies a contact. */ +/** Type-safe wrapper for an integer that uniquely identifies a contact. */ public class ContactId { private final int id; diff --git a/api/net/sf/briar/api/db/DatabaseComponent.java b/api/net/sf/briar/api/db/DatabaseComponent.java index 59261e2e1b9b6490259ace35300b59043a86c016..38a7bf68daa2eed9b3b50ab19c6c49456c5c6b08 100644 --- a/api/net/sf/briar/api/db/DatabaseComponent.java +++ b/api/net/sf/briar/api/db/DatabaseComponent.java @@ -35,7 +35,10 @@ public interface DatabaseComponent { /** Adds a locally generated message to the database. */ void addLocallyGeneratedMessage(Message m) throws DbException; - /** Generates a bundle of messages for the given contact. */ + /** + * Generates a bundle of acknowledgements, subscriptions, and batches of + * messages for the given contact. + */ void generateBundle(ContactId c, Bundle b) throws DbException; /** Returns the user's rating for the given author. */ @@ -45,8 +48,9 @@ public interface DatabaseComponent { Set<GroupId> getSubscriptions() throws DbException; /** - * Processes a bundle of messages received from the given contact. Some - * or all of the messages in the bundle may be stored. + * Processes a bundle of acknowledgements, subscriptions, and batches of + * messages received from the given contact. Some or all of the messages + * in the bundle may be stored. */ void receiveBundle(ContactId c, Bundle b) throws DbException; diff --git a/api/net/sf/briar/api/i18n/FontManager.java b/api/net/sf/briar/api/i18n/FontManager.java index df6be33d5b27817411545002416c53f62337ade6..13249bb41ad4a238a124f645d2788ff70e80638e 100644 --- a/api/net/sf/briar/api/i18n/FontManager.java +++ b/api/net/sf/briar/api/i18n/FontManager.java @@ -1,13 +1,12 @@ package net.sf.briar.api.i18n; import java.awt.Font; -import java.io.IOException; import java.util.Locale; public interface FontManager { /** Initializes the FontManager for the given locale. */ - void initialize(Locale locale) throws IOException; + void initialize(Locale locale); /** Returns the appropriate font for the given language. */ Font getFontForLanguage(String language); diff --git a/api/net/sf/briar/api/protocol/AuthorId.java b/api/net/sf/briar/api/protocol/AuthorId.java index 5f5ab3b1334cfff7c09d310a3fda022d35cbc9d5..8b32851a8b766e6e1198e1466decb730393c9444 100644 --- a/api/net/sf/briar/api/protocol/AuthorId.java +++ b/api/net/sf/briar/api/protocol/AuthorId.java @@ -2,7 +2,7 @@ package net.sf.briar.api.protocol; import java.util.Arrays; -/** Uniquely identifies a pseudonymous author. */ +/** Type-safe wrapper for a byte array that uniquely identifies an author. */ public class AuthorId { public static final int LENGTH = 32; diff --git a/api/net/sf/briar/api/protocol/BatchId.java b/api/net/sf/briar/api/protocol/BatchId.java index 9aad9c0e2db61e025e3779d66b57aec354ac3d3b..4b4578b03f32ace399bd6482e9206ce861cbe8f3 100644 --- a/api/net/sf/briar/api/protocol/BatchId.java +++ b/api/net/sf/briar/api/protocol/BatchId.java @@ -2,7 +2,10 @@ package net.sf.briar.api.protocol; import java.util.Arrays; -/** Uniquely identifies a batch of messages. */ +/** + * Type-safe wrapper for a byte array that uniquely identifies a batch of + * messages. + */ public class BatchId { public static final int LENGTH = 32; diff --git a/api/net/sf/briar/api/protocol/BundleId.java b/api/net/sf/briar/api/protocol/BundleId.java index 9dba86d20783c6a0ec61f238348840b58868ad72..2dd7e8f59f5483fa68cf0bc470a34eb429010b04 100644 --- a/api/net/sf/briar/api/protocol/BundleId.java +++ b/api/net/sf/briar/api/protocol/BundleId.java @@ -2,6 +2,10 @@ package net.sf.briar.api.protocol; import java.util.Arrays; +/** + * Type-safe wrapper for a byte array that uniquely identifies a bundle of + * acknowledgements, subscriptions, and batches of messages. + */ public class BundleId { public static final BundleId NONE = new BundleId(new byte[] { diff --git a/api/net/sf/briar/api/protocol/GroupId.java b/api/net/sf/briar/api/protocol/GroupId.java index 75a12c8a3f6222142647fda4db8d3854a40d6e6f..46c0456970c737cfaf5d057fe752f012750f4f86 100644 --- a/api/net/sf/briar/api/protocol/GroupId.java +++ b/api/net/sf/briar/api/protocol/GroupId.java @@ -2,7 +2,10 @@ package net.sf.briar.api.protocol; import java.util.Arrays; -/** Uniquely identifies a group to which a user may subscribe. */ +/** + * Type-safe wrapper for a byte array that uniquely identifies a group to which + * users may subscribe. + */ public class GroupId { public static final int LENGTH = 32; diff --git a/api/net/sf/briar/api/protocol/MessageId.java b/api/net/sf/briar/api/protocol/MessageId.java index ad25c171d6920ebda3238cfe9e62c01305564ca1..ee25caddc0f9a4e2b9b107e1b3c91859b6e0a350 100644 --- a/api/net/sf/briar/api/protocol/MessageId.java +++ b/api/net/sf/briar/api/protocol/MessageId.java @@ -2,7 +2,7 @@ package net.sf.briar.api.protocol; import java.util.Arrays; -/** Uniquely identifies a message. */ +/** Type-safe wrapper for a byte array that uniquely identifies a message. */ public class MessageId { /** Used to indicate that the first message in a thread has no parent. */ diff --git a/components/net/sf/briar/db/DatabaseComponentImpl.java b/components/net/sf/briar/db/DatabaseComponentImpl.java index ed6ba26912d3a307d4c4a988b672c14b329e0da5..2ed2467bb9ae93c985cd3a465ae769f2c8e912b7 100644 --- a/components/net/sf/briar/db/DatabaseComponentImpl.java +++ b/components/net/sf/briar/db/DatabaseComponentImpl.java @@ -15,6 +15,10 @@ import net.sf.briar.api.protocol.MessageId; import com.google.inject.Provider; +/** + * Abstract superclass containing code shared by ReadWriteLockDatabaseComponent + * and SynchronizedDatabaseComponent. + */ abstract class DatabaseComponentImpl<Txn> implements DatabaseComponent { private static final Logger LOG = @@ -35,9 +39,17 @@ abstract class DatabaseComponentImpl<Txn> implements DatabaseComponent { startCleaner(); } + /** + * Removes the oldest messages from the database, with a total size less + * than or equal to the given size. + */ protected abstract void expireMessages(long size) throws DbException; - // Locking: messages write + /** + * Calculates and returns the sendability score of a message. + * <p> + * Locking: messages write. + */ private int calculateSendability(Txn txn, Message m) throws DbException { int sendability = 0; // One point for a good rating @@ -51,6 +63,12 @@ abstract class DatabaseComponentImpl<Txn> implements DatabaseComponent { return sendability; } + /** + * Checks how much free storage space is available to the database, and if + * necessary expires old messages until the free space is at least + * MIN_FREE_SPACE. While the free space is less than CRITICAL_FREE_SPACE, + * operations that attempt to store messages in the database will block. + */ private void checkFreeSpaceAndClean() throws DbException { long freeSpace = db.getFreeSpace(); while(freeSpace < MIN_FREE_SPACE) { @@ -74,7 +92,11 @@ abstract class DatabaseComponentImpl<Txn> implements DatabaseComponent { } } - // Locking: contacts read + /** + * Returns true iff the database contains the given contact. + * <p> + * Locking: contacts read. + */ protected boolean containsContact(ContactId c) throws DbException { Txn txn = db.startTransaction(); try { @@ -87,14 +109,24 @@ abstract class DatabaseComponentImpl<Txn> implements DatabaseComponent { } } - // Locking: contacts read, messages write, messageStatuses write + /** + * Removes the given message (and all associated state) from the database. + * <p> + * Locking: contacts read, messages write, messageStatuses write. + */ protected void removeMessage(Txn txn, MessageId id) throws DbException { Integer sendability = db.getSendability(txn, id); assert sendability != null; + // If the message is sendable, deleting it may affect its ancestors' + // sendability (backward inclusion) if(sendability > 0) updateAncestorSendability(txn, id, false); db.removeMessage(txn, id); } + /** + * Returns true iff the amount of free storage space available to the + * database should be checked. + */ private boolean shouldCheckFreeSpace() { synchronized(spaceLock) { long now = System.currentTimeMillis(); @@ -117,6 +149,12 @@ abstract class DatabaseComponentImpl<Txn> implements DatabaseComponent { return false; } + /** + * Starts a new thread to monitor the amount of free storage space + * available to the database and expire old messages as necessary. + * <p> + * FIXME: The thread implementation should be factored out. + */ private void startCleaner() { Runnable cleaner = new Runnable() { public void run() { @@ -140,7 +178,14 @@ abstract class DatabaseComponentImpl<Txn> implements DatabaseComponent { new Thread(cleaner).start(); } - // Locking: contacts read, messages write, messageStatuses write + /** + * If the given message is already in the database, marks it as seen by the + * sender and returns false. Otherwise stores the message, updates the + * sendability of its ancestors if necessary, marks the message as seen by + * the sender and unseen by all other contacts, and returns true. + * <p> + * Locking: contacts read, messages write, messageStatuses write. + */ protected boolean storeMessage(Txn txn, Message m, ContactId sender) throws DbException { boolean added = db.addMessage(txn, m); @@ -164,7 +209,15 @@ abstract class DatabaseComponentImpl<Txn> implements DatabaseComponent { return added; } - // Locking: messages write + /** + * Iteratively updates the sendability of a message's ancestors to reflect + * a change in the message's sendability. Returns the number of ancestors + * that have changed from sendable to not sendable, or vice versa. + * <p> + * Locking: messages write. + * @param increment True if the message's sendability has changed from 0 to + * greater than 0, or false if it has changed from greater than 0 to 0. + */ private int updateAncestorSendability(Txn txn, MessageId m, boolean increment) throws DbException { int affected = 0; @@ -191,7 +244,14 @@ abstract class DatabaseComponentImpl<Txn> implements DatabaseComponent { return affected; } - // Locking: messages write + /** + * Updates the sendability of all messages written by the given author, and + * the ancestors of those messages if necessary. + * <p> + * Locking: messages write. + * @param increment True if the user's rating for the author has changed + * from not good to good, or false if it has changed from good to not good. + */ protected void updateAuthorSendability(Txn txn, AuthorId a, boolean increment) throws DbException { int direct = 0, indirect = 0; @@ -217,6 +277,11 @@ abstract class DatabaseComponentImpl<Txn> implements DatabaseComponent { + indirect + " indirectly"); } + /** + * Blocks until messages are allowed to be stored in the database. The + * storage of messages is not allowed while the amount of free storage + * space available to the database is less than CRITICAL_FREE_SPACE. + */ protected void waitForPermissionToWrite() { synchronized(writeLock) { while(!writesAllowed) { diff --git a/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java b/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java index f6d874fbe9aa024f826e26a28f42fda475b3fe9a..c1ead3ce907a4d1f5e754947276e91a9c9c745d9 100644 --- a/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java +++ b/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java @@ -21,6 +21,10 @@ import net.sf.briar.api.protocol.MessageId; import com.google.inject.Inject; import com.google.inject.Provider; +/** + * An implementation of DatabaseComponent using reentrant read-write locks. + * This implementation can allow writers to starve. + */ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { private static final Logger LOG = @@ -28,8 +32,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { /* * Locks must always be acquired in alphabetical order. See the Database - * interface to find out which calls require which locks. Note: this - * implementation can allow writers to starve. + * interface to find out which calls require which locks. */ private final ReentrantReadWriteLock contactLock = @@ -112,6 +115,8 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { try { Txn txn = db.startTransaction(); try { + // Don't store the message if the user has + // unsubscribed from the group if(db.containsSubscription(txn, m.getGroup())) { boolean added = storeMessage(txn, m, null); assert added; diff --git a/components/net/sf/briar/db/SynchronizedDatabaseComponent.java b/components/net/sf/briar/db/SynchronizedDatabaseComponent.java index 6e514a2c48eaa5ef45e56a335b46b47497b5ff14..b28841598baefa9b4cc00bad2afd131d9e3a5170 100644 --- a/components/net/sf/briar/db/SynchronizedDatabaseComponent.java +++ b/components/net/sf/briar/db/SynchronizedDatabaseComponent.java @@ -20,6 +20,10 @@ import net.sf.briar.api.protocol.MessageId; import com.google.inject.Inject; import com.google.inject.Provider; +/** + * An implementation of DatabaseComponent using Java synchronization. This + * implementation does not distinguish between readers and writers. + */ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { private static final Logger LOG = @@ -80,6 +84,8 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { synchronized(subscriptionLock) { Txn txn = db.startTransaction(); try { + // Don't store the message if the user has + // unsubscribed from the group if(db.containsSubscription(txn, m.getGroup())) { boolean added = storeMessage(txn, m, null); assert added; diff --git a/components/net/sf/briar/i18n/FontManagerImpl.java b/components/net/sf/briar/i18n/FontManagerImpl.java index 378cd354c26a7da87648671f0c957581ee28029c..e4877d59607f83b1a30dfd6848529bf5325c953d 100644 --- a/components/net/sf/briar/i18n/FontManagerImpl.java +++ b/components/net/sf/briar/i18n/FontManagerImpl.java @@ -12,6 +12,8 @@ import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.TreeMap; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.swing.UIManager; @@ -20,19 +22,32 @@ import net.sf.briar.util.FileUtils; public class FontManagerImpl implements FontManager { + private static final Logger LOG = + Logger.getLogger(FontManagerImpl.class.getName()); + + /** + * Each bundled font is associated with a size, which is meant to occupy + * roughly the same amount of space as the default font (12 point sans), + * and a list of languages for which the bundled font should be used. + */ private static final BundledFont[] BUNDLED_FONTS = { + // Use TibetanMachineUni for Tibetan new BundledFont("TibetanMachineUni.ttf", 14f, new String[] { "bo" }), + // Use Padauk for Burmese new BundledFont("Padauk.ttf", 14f, new String[] { "my" }), }; + // Map from languages to fonts private final Map<String, Font> fonts = new TreeMap<String, Font>(); private volatile Font defaultFont = null, uiFont = null; - public void initialize(Locale locale) throws IOException { - try { - ClassLoader loader = getClass().getClassLoader(); - for(BundledFont bf : BUNDLED_FONTS) { + public void initialize(Locale locale) { + // Look for bundled fonts in the jar and the filesystem. If any fonts + // are missing or fail to load, fall back to the default font. + ClassLoader loader = getClass().getClassLoader(); + for(BundledFont bf : BUNDLED_FONTS) { + try { InputStream in = loader.getResourceAsStream(bf.filename); if(in == null) { File root = FileUtils.getBriarDirectory(); @@ -42,9 +57,13 @@ public class FontManagerImpl implements FontManager { Font font = Font.createFont(Font.TRUETYPE_FONT, in); font = font.deriveFont(bf.size); for(String language : bf.languages) fonts.put(language, font); + } catch(FontFormatException e) { + if(LOG.isLoggable(Level.WARNING)) + LOG.warning("Could not load font: " + e.getMessage()); + } catch(IOException e) { + if(LOG.isLoggable(Level.WARNING)) + LOG.warning("Could not load font: " + e.getMessage()); } - } catch(FontFormatException e) { - throw new IOException(e); } defaultFont = getFont("Sans", 12f); assert defaultFont != null; // FIXME: This is failing on Windows diff --git a/components/net/sf/briar/i18n/I18nImpl.java b/components/net/sf/briar/i18n/I18nImpl.java index af09abf9d062891d52ac846662fcef4c5b8331ac..f4095a35940de9ddffd77de2c0448ffe391f4376 100644 --- a/components/net/sf/briar/i18n/I18nImpl.java +++ b/components/net/sf/briar/i18n/I18nImpl.java @@ -23,6 +23,11 @@ import net.sf.briar.util.FileUtils; public class I18nImpl implements I18n { + /** + * Property keys for strings used in the JRE's built-in dialogs. Values + * assigned to these keys in i18n properties files will override the + * built-in values. + */ private static final String[] uiManagerKeys = { "FileChooser.acceptAllFileFilterText", "FileChooser.cancelButtonText", @@ -87,6 +92,7 @@ public class I18nImpl implements I18n { synchronized(bundleLock) { if(bundle == null) { bundle = ResourceBundle.getBundle("i18n", locale, loader); + assert bundle != null; for(String key : uiManagerKeys) { try { UIManager.put(key, bundle.getString(key)); @@ -106,6 +112,7 @@ public class I18nImpl implements I18n { Font uiFont = fontManager.getUiFont(); synchronized(bundleLock) { this.locale = locale; + Locale.setDefault(locale); bundle = null; synchronized(listeners) { for(Listener l : listeners) l.localeChanged(uiFont); @@ -116,7 +123,7 @@ public class I18nImpl implements I18n { public void loadLocale() throws IOException { File root = FileUtils.getBriarDirectory(); Scanner s = new Scanner(new File(root, "Data/locale.cfg")); - if(s.hasNextLine()) locale = new Locale(s.nextLine()); + if(s.hasNextLine()) setLocale(new Locale(s.nextLine())); s.close(); } diff --git a/components/net/sf/briar/invitation/InvitationWorker.java b/components/net/sf/briar/invitation/InvitationWorker.java index ce3c5751cc7f153397049bd3345e336b97951a7a..e9ef25eef2b7bdef2cff85005c56131e6a320182 100644 --- a/components/net/sf/briar/invitation/InvitationWorker.java +++ b/components/net/sf/briar/invitation/InvitationWorker.java @@ -41,18 +41,11 @@ class InvitationWorker implements Runnable { List<File> files = new ArrayList<File>(); try { if(callback.isCancelled()) return; - File invitationDat = createInvitationDat(dir); - files.add(invitationDat); + files.add(createInvitationDat(dir)); if(callback.isCancelled()) return; - if(parameters.shouldCreateExe()) { - File briarExe = createBriarExe(dir); - files.add(briarExe); - } + if(parameters.shouldCreateExe()) files.add(createBriarExe(dir)); if(callback.isCancelled()) return; - if(parameters.shouldCreateJar()) { - File briarJar = createBriarJar(dir); - files.add(briarJar); - } + if(parameters.shouldCreateJar()) files.add(createBriarJar(dir)); } catch(IOException e) { callback.error(e.getMessage()); return; @@ -69,8 +62,7 @@ class InvitationWorker implements Runnable { // FIXME: Create a real invitation try { Thread.sleep(2000); - } catch(InterruptedException ignored) { - } + } catch(InterruptedException ignored) {} Arrays.fill(password, (char) 0); FileOutputStream out = new FileOutputStream(invitationDat); byte[] buf = new byte[1024]; diff --git a/components/net/sf/briar/protocol/BatchImpl.java b/components/net/sf/briar/protocol/BatchImpl.java index be84a7594f6b26f1f3fc719483ff61495c49a179..68d0e12ddc1e7dc000d025be5dbee34e4b38ce91 100644 --- a/components/net/sf/briar/protocol/BatchImpl.java +++ b/components/net/sf/briar/protocol/BatchImpl.java @@ -8,6 +8,7 @@ import net.sf.briar.api.protocol.Batch; import net.sf.briar.api.protocol.BatchId; import net.sf.briar.api.protocol.Message; +/** A simple in-memory implementation of a batch. */ class BatchImpl implements Batch { private final List<Message> messages = new ArrayList<Message>(); diff --git a/test/build.xml b/test/build.xml index 9c90d42b1299ec8fe1954b8264ed9e8068abcbc8..17dd1da67a7f0693cfd41abd1628f8a4131e8311 100644 --- a/test/build.xml +++ b/test/build.xml @@ -1,7 +1,7 @@ <project name='test' default='test'> <import file='../build-common.xml'/> <target name='test' depends='depend'> - <junit printsummary='on'> + <junit printsummary='on' showoutput='true'> <classpath> <fileset refid='bundled-jars'/> <fileset refid='test-jars'/> @@ -10,6 +10,8 @@ <path refid='test-classes'/> <path refid='util-classes'/> </classpath> + <test name='net.sf.briar.i18n.FontManagerImplTest'/> + <test name='net.sf.briar.invitation.InvitationWorkerTest'/> <test name='net.sf.briar.setup.SetupWorkerTest'/> <test name='net.sf.briar.util.FileUtilsTest'/> <test name='net.sf.briar.util.StringUtilsTest'/> diff --git a/test/net/sf/briar/TestUtils.java b/test/net/sf/briar/TestUtils.java index 86fb377099a0efa52e278be1beaf74b491c3b795..bd8397c85c1da84b6cd8d0032b5b8b32ef404908 100644 --- a/test/net/sf/briar/TestUtils.java +++ b/test/net/sf/briar/TestUtils.java @@ -11,7 +11,7 @@ public class TestUtils { private static final AtomicInteger nextTestDir = new AtomicInteger((int) (Math.random() * 1000 * 1000)); - public static void delete(File f) throws IOException { + public static void delete(File f) { if(f.isDirectory()) for(File child : f.listFiles()) delete(child); f.delete(); } @@ -29,4 +29,8 @@ public class TestUtils { File testDir = new File("test.tmp/" + name); return testDir; } + + public static void deleteTestDirectories() { + delete(new File("test.tmp")); + } } diff --git a/test/net/sf/briar/i18n/FontManagerImplTest.java b/test/net/sf/briar/i18n/FontManagerImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3c5dbd67593ac812d97cdf3ac83a06e08730a7ab --- /dev/null +++ b/test/net/sf/briar/i18n/FontManagerImplTest.java @@ -0,0 +1,69 @@ +package net.sf.briar.i18n; +import java.awt.Font; +import java.util.Locale; + +import junit.framework.TestCase; +import net.sf.briar.i18n.FontManagerImpl; + +import org.junit.Test; + +public class FontManagerImplTest extends TestCase { + + @Test + public void testBundledFontsAreLoaded() { + FontManagerImpl fontManager = new FontManagerImpl(); + fontManager.initialize(Locale.UK); + + Font font = fontManager.getFontForLanguage("en"); // English + assertEquals(12, font.getSize()); + + font = fontManager.getFontForLanguage("bo"); // Tibetan + assertEquals("Tibetan Machine Uni", font.getFamily()); + assertEquals(14, font.getSize()); + + font = fontManager.getFontForLanguage("my"); // Burmese + assertEquals("Padauk", font.getFamily()); + assertEquals(14, font.getSize()); + } + + @Test + public void testInternationalCharactersCanBeDisplayed() { + FontManagerImpl fontManager = new FontManagerImpl(); + fontManager.initialize(Locale.UK); + + Font font = fontManager.getFontForLanguage("en"); // English + assertTrue(font.canDisplay('a')); + + font = fontManager.getFontForLanguage("ar"); // Arabic + assertTrue(font.canDisplay('\u0627')); // An Arabic character + + font = fontManager.getFontForLanguage("bo"); // Tibetan + assertTrue(font.canDisplay('\u0f00')); // A Tibetan character + + font = fontManager.getFontForLanguage("fa"); // Persian + assertTrue(font.canDisplay('\ufb56')); // A Persian character + + font = fontManager.getFontForLanguage("my"); // Burmese + assertTrue(font.canDisplay('\u1000')); // A Burmese character + + font = fontManager.getFontForLanguage("zh"); // Chinese + assertTrue(font.canDisplay('\u4e00')); // A Chinese character + } + + @Test + public void testSetAndGetUiFont() { + FontManagerImpl fontManager = new FontManagerImpl(); + fontManager.initialize(Locale.UK); + Font font = fontManager.getUiFont(); + assertEquals(12, font.getSize()); + + fontManager.setUiFontForLanguage("bo"); + font = fontManager.getUiFont(); + assertEquals("Tibetan Machine Uni", font.getFamily()); + assertEquals(14, font.getSize()); + + fontManager.setUiFontForLanguage("en"); + font = fontManager.getUiFont(); + assertEquals(12, font.getSize()); + } +} diff --git a/test/net/sf/briar/invitation/InvitationWorkerTest.java b/test/net/sf/briar/invitation/InvitationWorkerTest.java index 2424580ef56d27c13c50d4e80ad64ca0878d1e8a..3090f71f67c48129e681dd08e52dfb6b1dcb109d 100644 --- a/test/net/sf/briar/invitation/InvitationWorkerTest.java +++ b/test/net/sf/briar/invitation/InvitationWorkerTest.java @@ -147,7 +147,7 @@ public class InvitationWorkerTest extends TestCase { } @After - public void tearDown() throws IOException { - TestUtils.delete(testDir); + public void tearDown() { + TestUtils.deleteTestDirectories(); } } diff --git a/test/net/sf/briar/setup/SetupWorkerTest.java b/test/net/sf/briar/setup/SetupWorkerTest.java index cf164a9591e8233c1e3983f5dc36ea7fff76bf7e..f311b8386928b8c5edc5855d0789099dfaa413b5 100644 --- a/test/net/sf/briar/setup/SetupWorkerTest.java +++ b/test/net/sf/briar/setup/SetupWorkerTest.java @@ -165,7 +165,7 @@ public class SetupWorkerTest extends TestCase { } @After - public void tearDown() throws IOException { - TestUtils.delete(testDir); + public void tearDown() { + TestUtils.deleteTestDirectories(); } } diff --git a/test/net/sf/briar/util/FileUtilsTest.java b/test/net/sf/briar/util/FileUtilsTest.java index 8dbbe0f367883712d498482f30daa5332baa86f9..8874a306b8b69698104d12aac67dd7537bb7dbc5 100644 --- a/test/net/sf/briar/util/FileUtilsTest.java +++ b/test/net/sf/briar/util/FileUtilsTest.java @@ -159,7 +159,7 @@ public class FileUtilsTest extends TestCase { } @After - public void tearDown() throws IOException { - TestUtils.delete(testDir); + public void tearDown() { + TestUtils.deleteTestDirectories(); } } diff --git a/test/net/sf/briar/util/ZipUtilsTest.java b/test/net/sf/briar/util/ZipUtilsTest.java index c2ad772aef9687654b3f2e6439f8fd8d8f43a0a4..613c87cd74cd6ff7a3ec84bf6a0bdfb4aff94b13 100644 --- a/test/net/sf/briar/util/ZipUtilsTest.java +++ b/test/net/sf/briar/util/ZipUtilsTest.java @@ -195,7 +195,7 @@ public class ZipUtilsTest extends TestCase { } @After - public void tearDown() throws IOException { - TestUtils.delete(testDir); + public void tearDown() { + TestUtils.deleteTestDirectories(); } } diff --git a/util/net/sf/briar/util/FileUtils.java b/util/net/sf/briar/util/FileUtils.java index 74757138921aa022887affa7268ef733fab9b49a..e8052c79f494e1b55b0fd5ea5153e9e6b6801863 100644 --- a/util/net/sf/briar/util/FileUtils.java +++ b/util/net/sf/briar/util/FileUtils.java @@ -18,19 +18,12 @@ public class FileUtils { assert f.exists(); if(f.isFile()) { // Running from a jar - return the jar's grandparent - try { - f = f.getCanonicalFile().getParentFile().getParentFile(); - } catch(IOException e) { - throw new RuntimeException(e); - } + f = f.getParentFile().getParentFile(); } else { - // Running from Eclipse - try { - f = new File(f.getCanonicalFile().getParentFile(), "Briar"); - } catch(IOException e) { - throw new RuntimeException(e); - } - f.mkdir(); + // Running from Eclipse or ant + f = new File(f.getParentFile(), "Briar"); + if(!f.exists()) + f = new File(f.getParentFile().getParentFile(), "Briar"); // Ant } assert f.exists(); assert f.isDirectory();