diff --git a/api/build.gradle b/api/build.gradle index 5ce85aebed..33f1e82aaa 100644 --- a/api/build.gradle +++ b/api/build.gradle @@ -23,6 +23,8 @@ android { dependencies { api fileTree(dir: 'libs', include: ['*.jar']) + implementation 'androidx.annotation:annotation:1.0.0' + testImplementation 'junit:junit:4.12' } // Borrowed from here: diff --git a/api/src/main/java/com/ichi2/anki/api/Utils.java b/api/src/main/java/com/ichi2/anki/api/Utils.java index 543dca9348..90b576e50c 100644 --- a/api/src/main/java/com/ichi2/anki/api/Utils.java +++ b/api/src/main/java/com/ichi2/anki/api/Utils.java @@ -18,18 +18,20 @@ package com.ichi2.anki.api; import android.text.Html; -import android.text.TextUtils; import java.math.BigInteger; import java.security.MessageDigest; +import java.util.Iterator; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import androidx.annotation.VisibleForTesting; + /** * Utilities class for the API */ -class Utils { +public class Utils { // Regex pattern used in removing tags from text before checksum private static final Pattern stylePattern = Pattern.compile("(?s).*?"); private static final Pattern scriptPattern = Pattern.compile("(?s).*?"); @@ -38,15 +40,17 @@ class Utils { private static final Pattern htmlEntitiesPattern = Pattern.compile("&#?\\w+;"); + @VisibleForTesting static String joinFields(String[] list) { - return list != null ? TextUtils.join("\u001f", list): null; + return list != null ? join("\u001f", list) : null; } - + @VisibleForTesting static String[] splitFields(String fields) { - return fields != null? fields.split("\\x1f", -1): null; + return fields != null ? fields.split("\\x1f", -1) : null; } + @VisibleForTesting static String joinTags(Set tags) { if (tags == null || tags.isEmpty()) { return ""; @@ -54,9 +58,10 @@ class Utils { for (String t : tags) { t.replaceAll(" ", "_"); } - return TextUtils.join(" ", tags); + return join(" ", tags); } + @VisibleForTesting static String[] splitTags(String tags) { if (tags == null) { return null; @@ -64,6 +69,7 @@ class Utils { return tags.trim().split("\\s+"); } + @VisibleForTesting static Long fieldChecksum(String data) { data = stripHTMLMedia(data); try { @@ -71,14 +77,14 @@ class Utils { byte[] digest = md.digest(data.getBytes("UTF-8")); BigInteger biginteger = new BigInteger(1, digest); String result = biginteger.toString(16); - + // pad checksum to 40 bytes, as is done in the main AnkiDroid code if (result.length() < 40) { String zeroes = "0000000000000000000000000000000000000000"; result = zeroes.substring(0, zeroes.length() - result.length()) + result; } - - return Long.valueOf(result.substring(0, 8), 16); + + return Long.valueOf(result.substring(0, 8), 16); } catch (Exception e) { // This is guaranteed to never happen throw new IllegalStateException("Error making field checksum with SHA1 algorithm and UTF-8 encoding", e); @@ -108,6 +114,7 @@ class Utils { * This should only affect substrings of the form &something; and not tags. * Internet rumour says that Html.fromHtml() doesn't cover all cases, but it doesn't get less * vague than that. + * * @param html The HTML escaped text * @return The text with its HTML entities unescaped. */ @@ -125,4 +132,32 @@ class Utils { htmlEntities.appendTail(sb); return sb.toString(); } + + static String join(CharSequence delimiter, Object[] tokens) { + StringBuilder sb = new StringBuilder(); + boolean firstTime = true; + for (Object token : tokens) { + if (firstTime) { + firstTime = false; + } else { + sb.append(delimiter); + } + sb.append(token); + } + return sb.toString(); + } + + static String join(CharSequence delimiter, Iterable tokens) { + StringBuilder sb = new StringBuilder(); + Iterator it = tokens.iterator(); + if (it.hasNext()) { + sb.append(it.next()); + while (it.hasNext()) { + sb.append(delimiter); + sb.append(it.next()); + } + } + return sb.toString(); + } + } diff --git a/api/src/test/java/com/ichi2/anki/api/ApiUtilsTest.java b/api/src/test/java/com/ichi2/anki/api/ApiUtilsTest.java new file mode 100644 index 0000000000..574892231a --- /dev/null +++ b/api/src/test/java/com/ichi2/anki/api/ApiUtilsTest.java @@ -0,0 +1,115 @@ +package com.ichi2.anki.api; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.HashSet; +import java.util.Set; + +import static junit.framework.Assert.assertEquals; + +/** + * Created by rodrigobresan on 19/10/17. + *

+ * In case of any questions, feel free to ask me + *

+ * E-mail: rcbresan@gmail.com + * Slack: bresan + */ + +@RunWith(JUnit4.class) +public class ApiUtilsTest { + + private static String delimiter = "\u001F"; + + @Test + public void joinFieldsShouldJoinWhenListIsValid() { + String fieldList[] = {"A", "B", "C"}; + String output = Utils.joinFields(fieldList); + + assertEquals("A" + delimiter + "B" + delimiter + "C", output); + } + + @Test + public void joinFieldsShouldReturnNullWhenListIsNull() { + String fieldList[] = null; + String output = Utils.joinFields(fieldList); + + assertEquals(null, output); + } + + @Test + public void splitFieldsShouldSplitRightWhenStringIsValid() { + String fieldList = "A" + delimiter + "B" + delimiter + "C"; + String output[] = Utils.splitFields(fieldList); + + assertEquals("A", output[0]); + assertEquals("B", output[1]); + assertEquals("C", output[2]); + } + + @Test + public void splitFieldsShouldReturnNullWhenStringIsNull() { + String fieldList = null; + String output[] = Utils.splitFields(fieldList); + + assertEquals(null, output); + } + + @Test + public void joinTagsShouldReturnEmptyStringWhenSetIsValid() { + Set set = new HashSet<>(); + set.add("A"); + set.add("B"); + set.add("C"); + + String output = Utils.joinTags(set); + + assertEquals("A B C", output); + } + + @Test + public void joinTagsShouldReturnEmptyStringWhenSetIsNull() { + Set set = null; + String output = Utils.joinTags(set); + + assertEquals("", output); + } + + @Test + public void joinTagsShouldReturnEmptyStringWhenSetIsEmpty() { + Set set = new HashSet<>(); + String output = Utils.joinTags(set); + + assertEquals("", output); + } + + @Test + public void splitTagsShouldReturnNullWhenStringIsValid() { + String tags = "A B C"; + String[] output = Utils.splitTags(tags); + + assertEquals("A", output[0]); + assertEquals("B", output[1]); + assertEquals("C", output[2]); + } + + @Test + public void splitTagsShouldReturnNullWhenStringIsNull() { + String tags = null; + String[] output = Utils.splitTags(tags); + + assertEquals(null, output); + } + + @Test + public void shouldGenerateProperCheckSum() { + String input = "AnkiDroid"; + + Long checkSum = Utils.fieldChecksum(input); + assertEquals(Long.valueOf(3533307532l), Long.valueOf(checkSum)); + } + + +}