Skip to content

Commit 4583b66

Browse files
kasnderclaude
andauthored
Add blocking logic tests for edge cases and InternetBlocklist (#536)
- TrackerBlocklist: test unknown UID defaults to blocked, block/unblock on nonexistent UID behavior, Content tracker handling in strict vs non-strict, multi-UID independence, AND-logic for category+tracker keys - BlockingModeLogic: test minimal mode ignores granular rules, all non-Content categories blocked in minimal, standard granular=false passthrough, end-to-end tests combining TrackerBlocklist with BlockingModeLogic for each mode - New InternetBlocklistTest: block/unblock cycle, per-UID independence, clear semantics, idempotent operations https://claude.ai/code/session_01GHJp37tHk1yJRVFmCjwCqi Co-authored-by: Claude <noreply@anthropic.com>
1 parent a150003 commit 4583b66

3 files changed

Lines changed: 291 additions & 0 deletions

File tree

app/src/test/java/net/kollnig/missioncontrol/data/BlockingModeLogicTest.java

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,4 +130,123 @@ public void clearingAutoExcludedAppRemovesItFromManagedSet() {
130130
assertEquals(Set.of("browser"),
131131
BlockingModeLogic.clearAutoExcludedApp(Set.of("browser", "other"), "other"));
132132
}
133+
134+
@Test
135+
public void minimalIgnoresGranularRuleParameter() {
136+
// Even when blockedByGranularRule is false, minimal still blocks non-Content
137+
assertTrue(BlockingModeLogic.shouldBlockKnownTracker(
138+
BlockingModeLogic.MODE_MINIMAL,
139+
"Advertising",
140+
false));
141+
// Even when blockedByGranularRule is true, minimal still allows Content
142+
assertFalse(BlockingModeLogic.shouldBlockKnownTracker(
143+
BlockingModeLogic.MODE_MINIMAL,
144+
BlockingModeLogic.CONTENT_CATEGORY,
145+
true));
146+
}
147+
148+
@Test
149+
public void minimalBlocksAllNonContentCategories() {
150+
for (String category : new String[]{
151+
"Advertising", "Analytics", "Social", "Fingerprinting",
152+
"Email", "Uncategorised"}) {
153+
assertTrue("Should block " + category,
154+
BlockingModeLogic.shouldBlockKnownTracker(
155+
BlockingModeLogic.MODE_MINIMAL, category, false));
156+
}
157+
}
158+
159+
@Test
160+
public void standardWithGranularFalseDoesNotBlock() {
161+
// In standard mode, if granular rule says not blocked, it's not blocked
162+
// regardless of category
163+
assertFalse(BlockingModeLogic.shouldBlockKnownTracker(
164+
BlockingModeLogic.MODE_STANDARD,
165+
"Advertising",
166+
false));
167+
assertFalse(BlockingModeLogic.shouldBlockKnownTracker(
168+
BlockingModeLogic.MODE_STANDARD,
169+
"Analytics",
170+
false));
171+
}
172+
173+
@Test
174+
public void strictWithGranularTrueBlocksContent() {
175+
assertTrue(BlockingModeLogic.shouldBlockKnownTracker(
176+
BlockingModeLogic.MODE_STRICT,
177+
BlockingModeLogic.CONTENT_CATEGORY,
178+
true));
179+
}
180+
181+
@Test
182+
public void endToEndMinimalBlocksAdvertisingTrackerRegardlessOfBlocklist() {
183+
// Simulates the full decision chain for minimal mode:
184+
// TrackerBlocklist is not consulted, only category matters
185+
TrackerBlocklist.resetForTests();
186+
TrackerBlocklist blocklist = TrackerBlocklist.getInstance(null);
187+
Tracker tracker = new Tracker("Branch", "Advertising");
188+
189+
// Even if user hasn't set up any rules (no ensureDefaults), minimal blocks
190+
boolean blockedByGranular = blocklist.blockedTracker(1001, tracker);
191+
assertTrue(BlockingModeLogic.shouldBlockKnownTracker(
192+
BlockingModeLogic.MODE_MINIMAL,
193+
tracker.category,
194+
blockedByGranular));
195+
}
196+
197+
@Test
198+
public void endToEndStandardContentTrackerAllowedByDefault() {
199+
// Simulates: standard mode + default blocklist = Content trackers pass through
200+
TrackerBlocklist.resetForTests();
201+
TrackerBlocklist blocklist = TrackerBlocklist.getInstance(null);
202+
Tracker contentTracker = new Tracker("Akamai", "Content");
203+
int uid = 1001;
204+
205+
blocklist.ensureDefaults(uid, false);
206+
boolean blockedByGranular = blocklist.blockedTracker(uid, contentTracker);
207+
assertFalse("Content should not be blocked by granular rule in standard defaults",
208+
blockedByGranular);
209+
assertFalse(BlockingModeLogic.shouldBlockKnownTracker(
210+
BlockingModeLogic.MODE_STANDARD,
211+
contentTracker.category,
212+
blockedByGranular));
213+
}
214+
215+
@Test
216+
public void endToEndStrictContentTrackerBlocked() {
217+
// Simulates: strict mode + strict blocklist = Content trackers blocked
218+
TrackerBlocklist.resetForTests();
219+
TrackerBlocklist blocklist = TrackerBlocklist.getInstance(null);
220+
Tracker contentTracker = new Tracker("Akamai", "Content");
221+
int uid = 1001;
222+
223+
blocklist.ensureDefaults(uid, true);
224+
boolean blockedByGranular = blocklist.blockedTracker(uid, contentTracker);
225+
assertTrue("Content should be blocked by granular rule in strict defaults",
226+
blockedByGranular);
227+
assertTrue(BlockingModeLogic.shouldBlockKnownTracker(
228+
BlockingModeLogic.MODE_STRICT,
229+
contentTracker.category,
230+
blockedByGranular));
231+
}
232+
233+
@Test
234+
public void endToEndStandardUserUnblocksSpecificTracker() {
235+
// User unblocks a specific tracker in standard mode -> it passes through
236+
TrackerBlocklist.resetForTests();
237+
TrackerBlocklist blocklist = TrackerBlocklist.getInstance(null);
238+
Tracker tracker = new Tracker("Branch", "Advertising");
239+
int uid = 1001;
240+
241+
blocklist.ensureDefaults(uid, false);
242+
assertTrue(blocklist.blockedTracker(uid, tracker));
243+
244+
// User unblocks Branch specifically
245+
blocklist.unblock(uid, tracker);
246+
boolean blockedByGranular = blocklist.blockedTracker(uid, tracker);
247+
assertFalse(BlockingModeLogic.shouldBlockKnownTracker(
248+
BlockingModeLogic.MODE_STANDARD,
249+
tracker.category,
250+
blockedByGranular));
251+
}
133252
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* TrackerControl is free software: you can redistribute it and/or modify
3+
* it under the terms of the GNU General Public License as published by
4+
* the Free Software Foundation, either version 3 of the License, or
5+
* (at your option) any later version.
6+
*
7+
* TrackerControl is distributed in the hope that it will be useful,
8+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
9+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10+
* GNU General Public License for more details.
11+
*
12+
* Copyright © 2026
13+
*/
14+
15+
package net.kollnig.missioncontrol.data;
16+
17+
import static org.junit.Assert.assertFalse;
18+
import static org.junit.Assert.assertTrue;
19+
20+
import org.junit.Before;
21+
import org.junit.Test;
22+
23+
import java.lang.reflect.Field;
24+
25+
public class InternetBlocklistTest {
26+
27+
@Before
28+
public void setUp() throws Exception {
29+
Field instance = InternetBlocklist.class.getDeclaredField("instance");
30+
instance.setAccessible(true);
31+
instance.set(null, null);
32+
}
33+
34+
@Test
35+
public void freshBlocklistAllowsAllUids() {
36+
InternetBlocklist blocklist = InternetBlocklist.getInstance(null);
37+
assertFalse(blocklist.blockedInternet(1001));
38+
assertFalse(blocklist.blockedInternet(9999));
39+
}
40+
41+
@Test
42+
public void blockAndUnblockCycle() {
43+
InternetBlocklist blocklist = InternetBlocklist.getInstance(null);
44+
45+
blocklist.block(1001);
46+
assertTrue(blocklist.blockedInternet(1001));
47+
48+
blocklist.unblock(1001);
49+
assertFalse(blocklist.blockedInternet(1001));
50+
}
51+
52+
@Test
53+
public void blockIsPerUid() {
54+
InternetBlocklist blocklist = InternetBlocklist.getInstance(null);
55+
56+
blocklist.block(1001);
57+
assertTrue(blocklist.blockedInternet(1001));
58+
assertFalse(blocklist.blockedInternet(1002));
59+
}
60+
61+
@Test
62+
public void clearRemovesAllEntries() {
63+
InternetBlocklist blocklist = InternetBlocklist.getInstance(null);
64+
65+
blocklist.block(1001);
66+
blocklist.block(1002);
67+
blocklist.clear();
68+
69+
assertFalse(blocklist.blockedInternet(1001));
70+
assertFalse(blocklist.blockedInternet(1002));
71+
}
72+
73+
@Test
74+
public void doubleBlockIsIdempotent() {
75+
InternetBlocklist blocklist = InternetBlocklist.getInstance(null);
76+
77+
blocklist.block(1001);
78+
blocklist.block(1001);
79+
assertTrue(blocklist.blockedInternet(1001));
80+
81+
blocklist.unblock(1001);
82+
assertFalse(blocklist.blockedInternet(1001));
83+
}
84+
85+
@Test
86+
public void unblockOnNonBlockedUidIsNoOp() {
87+
InternetBlocklist blocklist = InternetBlocklist.getInstance(null);
88+
blocklist.unblock(9999);
89+
assertFalse(blocklist.blockedInternet(9999));
90+
}
91+
}

app/src/test/java/net/kollnig/missioncontrol/data/TrackerBlocklistTest.java

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,4 +121,85 @@ public Integer resolve(String packageName) {
121121
}
122122
}));
123123
}
124+
125+
@Test
126+
public void blockedReturnsTrueForUnknownUid() {
127+
TrackerBlocklist blocklist = TrackerBlocklist.getInstance(null);
128+
assertTrue(blocklist.blocked(9999, "Advertising"));
129+
assertTrue(blocklist.blocked(9999, "Advertising | Branch"));
130+
}
131+
132+
@Test
133+
public void blockOnNonexistentUidIsNoOp() {
134+
TrackerBlocklist blocklist = TrackerBlocklist.getInstance(null);
135+
blocklist.block(9999, "Advertising");
136+
assertFalse(blocklist.hasSubset(9999));
137+
assertTrue(blocklist.blocked(9999, "Advertising"));
138+
}
139+
140+
@Test
141+
public void unblockOnNonexistentUidCreatesEntry() {
142+
TrackerBlocklist blocklist = TrackerBlocklist.getInstance(null);
143+
blocklist.unblock(9999, "Advertising");
144+
assertTrue(blocklist.hasSubset(9999));
145+
assertFalse(blocklist.blocked(9999, "Advertising"));
146+
}
147+
148+
@Test
149+
public void contentTrackerNotBlockedWithNonStrictDefaults() {
150+
TrackerBlocklist blocklist = TrackerBlocklist.getInstance(null);
151+
Tracker contentTracker = new Tracker("Akamai", "Content");
152+
blocklist.ensureDefaults(UID, false);
153+
154+
assertFalse(blocklist.blockedTracker(UID, contentTracker));
155+
}
156+
157+
@Test
158+
public void contentTrackerBlockedWithStrictDefaults() {
159+
TrackerBlocklist blocklist = TrackerBlocklist.getInstance(null);
160+
Tracker contentTracker = new Tracker("Akamai", "Content");
161+
blocklist.ensureDefaults(UID, true);
162+
163+
assertTrue(blocklist.blockedTracker(UID, contentTracker));
164+
}
165+
166+
@Test
167+
public void multipleUidsHaveIndependentBlockingRules() {
168+
TrackerBlocklist blocklist = TrackerBlocklist.getInstance(null);
169+
Tracker tracker = new Tracker("Branch", "Advertising");
170+
171+
blocklist.ensureDefaults(UID, false);
172+
blocklist.ensureDefaults(UID + 1, false);
173+
174+
blocklist.unblock(UID, tracker);
175+
assertFalse(blocklist.blockedTracker(UID, tracker));
176+
assertTrue(blocklist.blockedTracker(UID + 1, tracker));
177+
}
178+
179+
@Test
180+
public void blockedTrackerRequiresBothCategoryAndKeyBlocked() {
181+
TrackerBlocklist blocklist = TrackerBlocklist.getInstance(null);
182+
Tracker tracker = new Tracker("Branch", "Advertising");
183+
blocklist.ensureDefaults(UID, false);
184+
185+
assertTrue(blocklist.blockedTracker(UID, tracker));
186+
187+
// Unblock category only -> tracker unblocked
188+
blocklist.unblock(UID, tracker.category);
189+
assertFalse(blocklist.blockedTracker(UID, tracker));
190+
191+
// Re-block category, unblock specific tracker -> still unblocked
192+
blocklist.block(UID, tracker.category);
193+
blocklist.unblock(UID, tracker);
194+
assertFalse(blocklist.blockedTracker(UID, tracker));
195+
196+
// Unblock both -> definitely unblocked
197+
blocklist.unblock(UID, tracker.category);
198+
assertFalse(blocklist.blockedTracker(UID, tracker));
199+
200+
// Re-block both -> blocked again
201+
blocklist.block(UID, tracker.category);
202+
blocklist.block(UID, tracker);
203+
assertTrue(blocklist.blockedTracker(UID, tracker));
204+
}
124205
}

0 commit comments

Comments
 (0)