Skip to content

Commit c9ac5a2

Browse files
authored
fix: avoid using system default locale (appium#2352)
For example, if the system default locale is Turkish ("tr_TR"), then `ClipboardContentType.PLAINTEXT` argument gets converted to "plaıntext", not "plaintext". 1. Replace `toLowerCase()` -> `toLowerCase(ROOT)` 2. Replace `toUpperCase()` -> `toUpperCase(ROOT)` "ROOT" is a specific locale that at least works well for latin letters (that are used in content types, enum names etc.)
1 parent 95f1913 commit c9ac5a2

20 files changed

+63
-35
lines changed

src/e2eAndroidTest/java/io/appium/java_client/android/AndroidScreenRecordTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import java.time.Duration;
88

9+
import static java.util.Locale.ROOT;
910
import static org.hamcrest.MatcherAssert.assertThat;
1011
import static org.hamcrest.Matchers.emptyString;
1112
import static org.hamcrest.Matchers.is;
@@ -26,7 +27,7 @@ public void verifyBasicScreenRecordingWorks() throws InterruptedException {
2627
.withTimeLimit(Duration.ofSeconds(5))
2728
);
2829
} catch (WebDriverException e) {
29-
if (e.getMessage().toLowerCase().contains("emulator")) {
30+
if (e.getMessage() != null && e.getMessage().toLowerCase(ROOT).contains("emulator")) {
3031
// screen recording only works on real devices
3132
return;
3233
}

src/main/java/io/appium/java_client/HasBrowserCheck.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import org.openqa.selenium.remote.CapabilityType;
88

99
import static com.google.common.base.Strings.isNullOrEmpty;
10+
import static java.util.Locale.ROOT;
1011
import static java.util.Objects.requireNonNull;
1112

1213
public interface HasBrowserCheck extends ExecutesMethod, HasCapabilities {
@@ -34,7 +35,7 @@ default boolean isBrowser() {
3435
}
3536
try {
3637
var context = ((SupportsContextSwitching) this).getContext();
37-
return context != null && !context.toUpperCase().contains(NATIVE_CONTEXT);
38+
return context != null && !context.toUpperCase(ROOT).contains(NATIVE_CONTEXT);
3839
} catch (WebDriverException e) {
3940
return false;
4041
}

src/main/java/io/appium/java_client/android/AndroidMobileCommandHelper.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121

2222
import java.util.Map;
2323

24+
import static java.util.Locale.ROOT;
25+
2426
/**
2527
* This util class helps to prepare parameters of Android-specific JSONWP
2628
* commands.
@@ -241,7 +243,7 @@ public class AndroidMobileCommandHelper extends MobileCommand {
241243
String phoneNumber, GsmCallActions gsmCallActions) {
242244
return Map.entry(GSM_CALL, Map.of(
243245
"phoneNumber", phoneNumber,
244-
"action", gsmCallActions.name().toLowerCase()
246+
"action", gsmCallActions.name().toLowerCase(ROOT)
245247
));
246248
}
247249

@@ -275,7 +277,7 @@ public class AndroidMobileCommandHelper extends MobileCommand {
275277
@Deprecated
276278
public static Map.Entry<String, Map<String, ?>> gsmVoiceCommand(
277279
GsmVoiceState gsmVoiceState) {
278-
return Map.entry(GSM_VOICE, Map.of("state", gsmVoiceState.name().toLowerCase()));
280+
return Map.entry(GSM_VOICE, Map.of("state", gsmVoiceState.name().toLowerCase(ROOT)));
279281
}
280282

281283
/**
@@ -289,7 +291,7 @@ public class AndroidMobileCommandHelper extends MobileCommand {
289291
@Deprecated
290292
public static Map.Entry<String, Map<String, ?>> networkSpeedCommand(
291293
NetworkSpeed networkSpeed) {
292-
return Map.entry(NETWORK_SPEED, Map.of("netspeed", networkSpeed.name().toLowerCase()));
294+
return Map.entry(NETWORK_SPEED, Map.of("netspeed", networkSpeed.name().toLowerCase(ROOT)));
293295
}
294296

295297
/**
@@ -317,7 +319,7 @@ public class AndroidMobileCommandHelper extends MobileCommand {
317319
@Deprecated
318320
public static Map.Entry<String, Map<String, ?>> powerACCommand(
319321
PowerACState powerACState) {
320-
return Map.entry(POWER_AC_STATE, Map.of("state", powerACState.name().toLowerCase()));
322+
return Map.entry(POWER_AC_STATE, Map.of("state", powerACState.name().toLowerCase(ROOT)));
321323
}
322324

323325
/**

src/main/java/io/appium/java_client/android/HasAndroidClipboard.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.util.Map;
2626

2727
import static io.appium.java_client.MobileCommand.SET_CLIPBOARD;
28+
import static java.util.Locale.ROOT;
2829
import static java.util.Objects.requireNonNull;
2930

3031
public interface HasAndroidClipboard extends HasClipboard {
@@ -39,7 +40,7 @@ default void setClipboard(String label, ClipboardContentType contentType, byte[]
3940
CommandExecutionHelper.execute(this, Map.entry(SET_CLIPBOARD,
4041
Map.of(
4142
"content", new String(requireNonNull(base64Content), StandardCharsets.UTF_8),
42-
"contentType", contentType.name().toLowerCase(),
43+
"contentType", contentType.name().toLowerCase(ROOT),
4344
"label", requireNonNull(label)
4445
)
4546
));

src/main/java/io/appium/java_client/android/SupportsSpecialEmulatorCommands.java

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import static io.appium.java_client.MobileCommand.POWER_AC_STATE;
1515
import static io.appium.java_client.MobileCommand.POWER_CAPACITY;
1616
import static io.appium.java_client.MobileCommand.SEND_SMS;
17+
import static java.util.Locale.ROOT;
1718

1819
public interface SupportsSpecialEmulatorCommands extends ExecutesMethod, CanRememberExtensionPresence {
1920

@@ -53,15 +54,15 @@ default void makeGsmCall(String phoneNumber, GsmCallActions gsmCallAction) {
5354
try {
5455
CommandExecutionHelper.executeScript(assertExtensionExists(extName), extName, Map.of(
5556
"phoneNumber", phoneNumber,
56-
"action", gsmCallAction.toString().toLowerCase()
57+
"action", gsmCallAction.toString().toLowerCase(ROOT)
5758
));
5859
} catch (UnsupportedCommandException e) {
5960
// TODO: Remove the fallback
6061
CommandExecutionHelper.execute(
6162
markExtensionAbsence(extName),
6263
Map.entry(GSM_CALL, Map.of(
6364
"phoneNumber", phoneNumber,
64-
"action", gsmCallAction.toString().toLowerCase()
65+
"action", gsmCallAction.toString().toLowerCase(ROOT)
6566
))
6667
);
6768
}
@@ -99,14 +100,14 @@ default void setGsmVoice(GsmVoiceState gsmVoiceState) {
99100
final String extName = "mobile: gsmVoice";
100101
try {
101102
CommandExecutionHelper.executeScript(assertExtensionExists(extName), extName, Map.of(
102-
"state", gsmVoiceState.toString().toLowerCase()
103+
"state", gsmVoiceState.toString().toLowerCase(ROOT)
103104
));
104105
} catch (UnsupportedCommandException e) {
105106
// TODO: Remove the fallback
106107
CommandExecutionHelper.execute(
107108
markExtensionAbsence(extName),
108109
Map.entry(GSM_VOICE, Map.of(
109-
"state", gsmVoiceState.name().toLowerCase()
110+
"state", gsmVoiceState.name().toLowerCase(ROOT)
110111
))
111112
);
112113
}
@@ -121,14 +122,14 @@ default void setNetworkSpeed(NetworkSpeed networkSpeed) {
121122
final String extName = "mobile: networkSpeed";
122123
try {
123124
CommandExecutionHelper.executeScript(assertExtensionExists(extName), extName, Map.of(
124-
"speed", networkSpeed.toString().toLowerCase()
125+
"speed", networkSpeed.toString().toLowerCase(ROOT)
125126
));
126127
} catch (UnsupportedCommandException e) {
127128
// TODO: Remove the fallback
128129
CommandExecutionHelper.execute(
129130
markExtensionAbsence(extName),
130131
Map.entry(NETWORK_SPEED, Map.of(
131-
"netspeed", networkSpeed.name().toLowerCase()
132+
"netspeed", networkSpeed.name().toLowerCase(ROOT)
132133
))
133134
);
134135
}
@@ -165,14 +166,14 @@ default void setPowerAC(PowerACState powerACState) {
165166
final String extName = "mobile: powerAC";
166167
try {
167168
CommandExecutionHelper.executeScript(assertExtensionExists(extName), extName, Map.of(
168-
"state", powerACState.toString().toLowerCase()
169+
"state", powerACState.toString().toLowerCase(ROOT)
169170
));
170171
} catch (UnsupportedCommandException e) {
171172
// TODO: Remove the fallback
172173
CommandExecutionHelper.execute(
173174
markExtensionAbsence(extName),
174175
Map.entry(POWER_AC_STATE, Map.of(
175-
"state", powerACState.name().toLowerCase()
176+
"state", powerACState.name().toLowerCase(ROOT)
176177
))
177178
);
178179
}

src/main/java/io/appium/java_client/clipboard/HasClipboard.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
import static io.appium.java_client.MobileCommand.GET_CLIPBOARD;
2929
import static io.appium.java_client.MobileCommand.SET_CLIPBOARD;
30+
import static java.util.Locale.ROOT;
3031
import static java.util.Objects.requireNonNull;
3132

3233
public interface HasClipboard extends ExecutesMethod, CanRememberExtensionPresence {
@@ -40,7 +41,7 @@ default void setClipboard(ClipboardContentType contentType, byte[] base64Content
4041
final String extName = "mobile: setClipboard";
4142
var args = Map.of(
4243
"content", new String(requireNonNull(base64Content), StandardCharsets.UTF_8),
43-
"contentType", contentType.name().toLowerCase()
44+
"contentType", contentType.name().toLowerCase(ROOT)
4445
);
4546
try {
4647
CommandExecutionHelper.executeScript(assertExtensionExists(extName), extName, args);
@@ -58,7 +59,7 @@ default void setClipboard(ClipboardContentType contentType, byte[] base64Content
5859
*/
5960
default String getClipboard(ClipboardContentType contentType) {
6061
final String extName = "mobile: getClipboard";
61-
var args = Map.of("contentType", contentType.name().toLowerCase());
62+
var args = Map.of("contentType", contentType.name().toLowerCase(ROOT));
6263
try {
6364
return CommandExecutionHelper.executeScript(assertExtensionExists(extName), extName, args);
6465
} catch (UnsupportedCommandException e) {

src/main/java/io/appium/java_client/driverscripts/ScriptOptions.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.HashMap;
2121
import java.util.Map;
2222

23+
import static java.util.Locale.ROOT;
2324
import static java.util.Objects.requireNonNull;
2425
import static java.util.Optional.ofNullable;
2526

@@ -59,7 +60,7 @@ public ScriptOptions withTimeout(long timeoutMs) {
5960
*/
6061
public Map<String, Object> build() {
6162
var map = new HashMap<String, Object>();
62-
ofNullable(scriptType).ifPresent(x -> map.put("type", x.name().toLowerCase()));
63+
ofNullable(scriptType).ifPresent(x -> map.put("type", x.name().toLowerCase(ROOT)));
6364
ofNullable(timeoutMs).ifPresent(x -> map.put("timeout", x));
6465
return Collections.unmodifiableMap(map);
6566
}

src/main/java/io/appium/java_client/gecko/options/SupportsVerbosityOption.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222

2323
import java.util.Optional;
2424

25+
import static java.util.Locale.ROOT;
26+
2527
public interface SupportsVerbosityOption<T extends BaseOptions<T>> extends
2628
Capabilities, CanSetCapability<T> {
2729
String VERBOSITY_OPTION = "verbosity";
@@ -34,7 +36,7 @@ public interface SupportsVerbosityOption<T extends BaseOptions<T>> extends
3436
* @return self instance for chaining.
3537
*/
3638
default T setVerbosity(Verbosity verbosity) {
37-
return amend(VERBOSITY_OPTION, verbosity.name().toLowerCase());
39+
return amend(VERBOSITY_OPTION, verbosity.name().toLowerCase(ROOT));
3840
}
3941

4042
/**
@@ -45,7 +47,7 @@ default T setVerbosity(Verbosity verbosity) {
4547
default Optional<Verbosity> getVerbosity() {
4648
return Optional.ofNullable(getCapability(VERBOSITY_OPTION))
4749
.map(String::valueOf)
48-
.map(String::toUpperCase)
50+
.map(verbosity -> verbosity.toUpperCase(ROOT))
4951
.map(Verbosity::valueOf);
5052
}
5153
}

src/main/java/io/appium/java_client/internal/filters/AppiumIdempotencyFilter.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
import org.openqa.selenium.remote.http.HttpHandler;
2121
import org.openqa.selenium.remote.http.HttpMethod;
2222

23-
import java.util.UUID;
23+
import static java.util.Locale.ROOT;
24+
import static java.util.UUID.randomUUID;
2425

2526
public class AppiumIdempotencyFilter implements Filter {
2627
// https://github.com/appium/appium-base-driver/pull/400
@@ -30,7 +31,7 @@ public class AppiumIdempotencyFilter implements Filter {
3031
public HttpHandler apply(HttpHandler next) {
3132
return req -> {
3233
if (req.getMethod() == HttpMethod.POST && req.getUri().endsWith("/session")) {
33-
req.setHeader(IDEMPOTENCY_KEY_HEADER, UUID.randomUUID().toString().toLowerCase());
34+
req.setHeader(IDEMPOTENCY_KEY_HEADER, randomUUID().toString().toLowerCase(ROOT));
3435
}
3536
return next.execute(req);
3637
};

src/main/java/io/appium/java_client/internal/filters/AppiumUserAgentFilter.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import org.openqa.selenium.remote.http.HttpHandler;
2525
import org.openqa.selenium.remote.http.HttpHeader;
2626

27+
import static java.util.Locale.ROOT;
28+
2729
/**
2830
* Manage Appium Client configurations.
2931
*/
@@ -54,7 +56,7 @@ private static String buildUserAgentHeaderValue(@NonNull String previousUA) {
5456
* like by this filter.
5557
*/
5658
private static boolean containsAppiumName(@Nullable String userAgent) {
57-
return userAgent != null && userAgent.toLowerCase().contains(USER_AGENT_PREFIX.toLowerCase());
59+
return userAgent != null && userAgent.toLowerCase(ROOT).contains(USER_AGENT_PREFIX.toLowerCase(ROOT));
5860
}
5961

6062
/**

0 commit comments

Comments
 (0)