Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions app/src/main/java/com/httrack/android/OptionsActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,8 @@ public static class BrowserId extends Tab {
@Fields({ R.id.checkAcceptCookies, R.id.editCookiesFile,
R.id.radioCheckDocumentType, R.id.checkParseJavaFiles, R.id.radioSpider,
R.id.checkSitemap, R.id.editSitemapUrl, R.id.checkUpdateHacks,
R.id.checkUrlHacks, R.id.checkTolerentRequests, R.id.checkForceHttp10 })
R.id.checkUrlHacks, R.id.editHostAlias, R.id.checkTolerentRequests,
R.id.checkForceHttp10 })
public static class Spider extends Tab {
}

Expand Down Expand Up @@ -246,8 +247,7 @@ public static class MimeDefs extends Tab {
@HelpPage("guide.html#droid/opt-experts-only")
@Fields({ R.id.checkUseCacheForUpdates, R.id.radioPrimaryScanRule,
R.id.textTravelMode, R.id.radioTravelMode, R.id.radioGlobalTravelMode,
R.id.radioRewriteLinks, R.id.editStripQuery, R.id.editHostAlias,
R.id.checkActivateDebugging })
R.id.radioRewriteLinks, R.id.editStripQuery, R.id.checkActivateDebugging })
public static class ExpertsOnly extends Tab {
}

Expand Down
13 changes: 0 additions & 13 deletions app/src/main/res/layout/activity_options_expertsonly.xml
Original file line number Diff line number Diff line change
Expand Up @@ -205,19 +205,6 @@
android:inputType="textNoSuggestions" />
</LinearLayout>

<TextView
android:id="@+id/textHostAlias"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/host_alias" />

<EditText
android:id="@+id/editHostAlias"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="@string/hint_host_alias"
android:inputType="textMultiLine|textNoSuggestions" />

<CheckBox
android:id="@+id/checkActivateDebugging"
android:layout_width="fill_parent"
Expand Down
13 changes: 13 additions & 0 deletions app/src/main/res/layout/activity_options_spider.xml
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,19 @@
android:layout_gravity="end"
android:text="@string/url_hacks" />

<TextView
android:id="@+id/textHostAlias"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/host_alias" />

<EditText
android:id="@+id/editHostAlias"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="@string/hint_host_alias"
android:inputType="textMultiLine|textNoSuggestions" />

<CheckBox
android:id="@+id/checkTolerentRequests"
android:layout_width="fill_parent"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,35 +37,6 @@ public class NumericFieldInputTypeTest {
{ "editMinTransferRate", "J" }, { "editProxyPort", "P" },
{ "editSingleFileMaxSize", "--single-file-max-size" } };

/* Unit tests run with the app project as working directory. */
private static File resDir() {
for (final String path : new String[] { "src/main/res",
"app/src/main/res" }) {
final File dir = new File(path);
if (dir.isDirectory()) {
return dir;
}
}
throw new IllegalStateException("no res directory below "
+ new File(".").getAbsolutePath());
}

/* Every layout, qualified variants such as layout-land/ included. */
private static Set<File> layouts() {
final Set<File> files = new HashSet<File>();
for (final File dir : resDir().listFiles()) {
if (!dir.isDirectory() || !dir.getName().startsWith("layout")) {
continue;
}
for (final File file : dir.listFiles()) {
if (file.getName().endsWith(".xml")) {
files.add(file);
}
}
}
return files;
}

private static String option(final String id) {
for (final String[] field : NUMERIC_FIELDS) {
if (field[0].equals(id)) {
Expand All @@ -82,7 +53,7 @@ public void numericFieldsOfferADecimalPointOnlyWhereTheEngineTakesOne()
factory.setNamespaceAware(true);
final DocumentBuilder builder = factory.newDocumentBuilder();
final Set<String> seen = new HashSet<String>();
for (final File layout : layouts()) {
for (final File layout : TestSources.layouts()) {
final Document doc = builder.parse(layout);
final NodeList nodes = doc.getElementsByTagName("*");
for (int i = 0; i < nodes.getLength(); i++) {
Expand Down
107 changes: 107 additions & 0 deletions app/src/test/java/com/httrack/android/OptionsTabFieldsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package com.httrack.android;

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.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.junit.Test;

/**
* A tab's declared field must be in that tab's own layout: leaving the tab
* looks it up, and WidgetDataExchange throws on a view it cannot find.
*/
public class OptionsTabFieldsTest {
/** @ActivityId + @Fields of one tab, as declared in the source. */
private static final Pattern TAB = Pattern.compile(
"@ActivityId\\(R\\.layout\\.(\\w+)\\)(.*?)public static class",
Pattern.DOTALL);

private static final Pattern FIELD_ID = Pattern.compile("R\\.id\\.(\\w+)");

private static final Pattern LAYOUT_ID = Pattern
.compile("android:id=\"@\\+?id/(\\w+)\"");

private static List<String> ids(final Pattern pattern, final String text) {
final List<String> ids = new ArrayList<String>();
final Matcher m = pattern.matcher(text);
while (m.find()) {
ids.add(m.group(1));
}
return ids;
}

/** Layout of each option tab, with the field ids that tab declares. */
private static Map<String, List<String>> tabs() throws IOException {
final Map<String, List<String>> tabs = new LinkedHashMap<String, List<String>>();
final Matcher tab = TAB.matcher(TestSources.javaSource("OptionsActivity"));
while (tab.find()) {
final List<String> fields = ids(FIELD_ID, tab.group(2));
// An annotation order this pattern cannot read would yield none.
assertFalse("no field parsed for " + tab.group(1), fields.isEmpty());
assertFalse("twice: " + tab.group(1), tabs.containsKey(tab.group(1)));
tabs.put(tab.group(1), fields);
}
assertTrue("parsed " + tabs.size() + " tabs", tabs.size() > 10);
return tabs;
}

@Test
public void everyTabFieldLivesInThatTabsLayout() throws IOException {
for (final Map.Entry<String, List<String>> tab : tabs().entrySet()) {
final List<File> files = TestSources.layouts(tab.getKey());
assertFalse("no layout named " + tab.getKey(), files.isEmpty());
for (final File file : files) {
final Set<String> declared = new HashSet<String>(ids(LAYOUT_ID,
TestSources.read(file)));
for (final String field : tab.getValue()) {
assertTrue(file.getName() + " has no " + field,
declared.contains(field));
}
}
}
}

/* The other direction: a view the mapper knows but no tab claims is never
saved, and nothing at runtime says so. */
@Test
public void everyMappedOptionViewIsClaimedByATab() throws IOException {
final Set<String> mapped = new HashSet<String>(ids(FIELD_ID,
TestSources.javaSource("OptionsMapper")));
assertTrue("parsed " + mapped.size() + " mapped ids", mapped.size() > 80);
final Map<String, List<String>> tabs = tabs();
int claimed = 0;
for (final File file : TestSources.layouts()) {
if (!file.getName().startsWith("activity_options_")) {
continue;
}
final String layout = file.getName().replaceFirst("\\.xml$", "");
for (final String id : ids(LAYOUT_ID, TestSources.read(file))) {
if (!mapped.contains(id)) {
continue;
}
final List<String> owners = new ArrayList<String>();
for (final Map.Entry<String, List<String>> tab : tabs.entrySet()) {
if (tab.getValue().contains(id)) {
owners.add(tab.getKey());
}
}
assertEquals(id + " in " + layout + " claimed by " + owners, 1,
owners.size());
assertEquals(id + " is claimed by " + owners.get(0), layout,
owners.get(0));
claimed++;
}
}
assertTrue("checked " + claimed + " views", claimed > 50);
}
}
58 changes: 58 additions & 0 deletions app/src/test/java/com/httrack/android/TestSources.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.httrack.android;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;

/** Checked-in sources the tests read; they run with the app project as
* working directory. */
final class TestSources {
private TestSources() {
}

private static File dir(final String path) {
for (final String prefix : new String[] { "", "app/" }) {
final File dir = new File(prefix + path);
if (dir.isDirectory()) {
return dir;
}
}
throw new IllegalStateException("no " + path + " below "
+ new File(".").getAbsolutePath());
}

/** Every layout, qualified variants such as layout-land/ included. */
static List<File> layouts() {
return layouts(null);
}

/** Every variant of NAME, all of which may be inflated; every layout when
* NAME is null. */
static List<File> layouts(final String name) {
final List<File> files = new ArrayList<File>();
for (final File res : dir("src/main/res").listFiles()) {
if (!res.isDirectory() || !res.getName().startsWith("layout")) {
continue;
}
for (final File file : res.listFiles()) {
if (name == null ? file.getName().endsWith(".xml")
: file.getName().equals(name + ".xml")) {
files.add(file);
}
}
}
return files;
}

static String read(final File file) throws IOException {
return new String(Files.readAllBytes(file.toPath()), "UTF-8");
}

/** Source of the com.httrack.android class NAME. */
static String javaSource(final String name) throws IOException {
return read(new File(dir("src/main/java"), "com/httrack/android/" + name
+ ".java"));
}
}
Loading