[Payment Request] Update the CurrencyStringFormatter to call the native impl.
Uses components/payments/currency_formatter.h by way of a JNI bridge.
Had to get rid of the junit tests because the implementation now require the
native libraries to be loaded.
BUG=679797
TEST=CurrencyStringFormatterTest, manual
Review-Url: https://codereview.chromium.org/2629883004
Cr-Commit-Position: refs/heads/master@{#443797}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/CurrencyFormatter.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/CurrencyFormatter.java
new file mode 100644
index 0000000..e3b9291
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/CurrencyFormatter.java
@@ -0,0 +1,79 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.payments;
+
+import android.support.annotation.Nullable;
+
+import org.chromium.base.annotations.JNINamespace;
+
+import java.util.Locale;
+
+/**
+ * Formatter for currency amounts.
+ * https://w3c.github.io/browser-payment-api/specs/paymentrequest.html#currencyamount
+ */
+@JNINamespace("payments")
+public class CurrencyFormatter {
+ /**
+ * Pointer to the native implementation.
+ */
+ private long mCurrencyFormatterAndroid;
+
+ /**
+ * Builds the formatter for the given currency code and the current user locale.
+ *
+ * @param currencyCode The currency code. Most commonly, this follows ISO 4217 format: 3 upper
+ * case ASCII letters. For example, "USD". Format is not restricted. Should
+ * not be null.
+ * @param currencySystem URI specifying the ISO4217 currency code specification. See for
+ * details: https://w3c.github.io/browser-payment-api/#paymentcurrencyamount-dictionary
+ * Can be null, in which case "urn:iso:std:iso:4217" is assumed.
+ * @param userLocale User's current locale. Should not be null.
+ */
+ public CurrencyFormatter(
+ String currencyCode, @Nullable String currencySystem, Locale userLocale) {
+ assert currencyCode != null : "currencyCode should not be null";
+ assert userLocale != null : "userLocale should not be null";
+
+ // Note that this technically leaks the native object.
+ mCurrencyFormatterAndroid = nativeInitCurrencyFormatterAndroid(
+ currencyCode, currencySystem == null ? "" : currencySystem, userLocale.toString());
+ }
+
+ /** Will destroy the native object. This class shouldn't be used afterwards. */
+ public void destroy() {
+ if (mCurrencyFormatterAndroid != 0) {
+ nativeDestroy(mCurrencyFormatterAndroid);
+ mCurrencyFormatterAndroid = 0;
+ }
+ }
+
+ /** @return The currency code formatted for display. */
+ public String getFormattedCurrencyCode() {
+ return nativeGetFormattedCurrencyCode(mCurrencyFormatterAndroid);
+ }
+
+ /**
+ * Formats the currency string for display. Does not parse the string into a number, because it
+ * might be too large. The number is formatted for the current locale and can include a
+ * currency symbol (e.g. $) anywhere in the string, but will not contain the currency code
+ * (e.g. USD/US). All spaces in the currency are unicode non-breaking space.
+ *
+ * @param amountValue The number to format. Should be in "^-?[0-9]+(\.[0-9]+)?$" format. Should
+ * not be null.
+ * @return The amount formatted with the specified currency. See description for details.
+ */
+ public String format(String amountValue) {
+ assert amountValue != null : "amountValue should not be null";
+
+ return nativeFormat(mCurrencyFormatterAndroid, amountValue);
+ }
+
+ private native long nativeInitCurrencyFormatterAndroid(
+ String currencyCode, String currencySystem, String localeName);
+ private native void nativeDestroy(long nativeCurrencyFormatterAndroid);
+ private native String nativeFormat(long nativeCurrencyFormatterAndroid, String amountValue);
+ private native String nativeGetFormattedCurrencyCode(long nativeCurrencyFormatterAndroid);
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/CurrencyStringFormatter.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/CurrencyStringFormatter.java
deleted file mode 100644
index 693bc03..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/CurrencyStringFormatter.java
+++ /dev/null
@@ -1,188 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.payments;
-
-import java.text.DecimalFormatSymbols;
-import java.util.Currency;
-import java.util.Locale;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Formatter for currency strings that can be too large to parse into numbers.
- * https://w3c.github.io/browser-payment-api/specs/paymentrequest.html#currencyamount
- */
-public class CurrencyStringFormatter {
- // Amount value pattern and capture group numbers.
- private static final String AMOUNT_VALUE_PATTERN = "^(-?)([0-9]+)(\\.([0-9]+))?$";
- private static final int OPTIONAL_NEGATIVE_GROUP = 1;
- private static final int DIGITS_BETWEEN_NEGATIVE_AND_PERIOD_GROUP = 2;
- private static final int DIGITS_AFTER_PERIOD_GROUP = 4;
-
- // Max currency code length. Maximum length of currency code can be at most 2048.
- private static final int MAX_CURRENCY_CODE_LEN = 2048;
-
- // Currency code exceeding 6 chars will be ellipsized during formatting for display.
- private static final int MAX_CURRENCY_CHARS = 6;
-
- // Unicode character for ellipsis.
- private static final String ELLIPSIS = "\u2026";
-
- // Formatting constants.
- private static final int DIGIT_GROUPING_SIZE = 3;
-
- private final Pattern mAmountValuePattern;
-
- /**
- * The currency formatted for display. Currency can be any string of at most
- * 2048 characters.Currency code more than 6 character is formatted to first
- * 5 characters and ellipsis.
- */
- public final String mFormattedCurrencyCode;
-
- /**
- * The symbol for the currency specified on the bill. For example, the symbol for "USD" is "$".
- */
- private final String mCurrencySymbol;
-
- /**
- * The number of digits after the decimal separator for the currency specified on the bill. For
- * example, 2 for "USD" and 0 for "JPY".
- */
- private final int mDefaultFractionDigits;
-
- /**
- * The number grouping separator for the current locale. For example, "," in US. 3-digit groups
- * are assumed.
- */
- private final char mGroupingSeparator;
-
- /**
- * The monetary decimal separator for the current locale. For example, "." in US and "," in
- * France.
- */
- private final char mMonetaryDecimalSeparator;
-
- /**
- * Builds the formatter for the given currency code and the current user locale.
- *
- * @param currencyCode The currency code. Most commonly, this follows ISO 4217 format: 3 upper
- * case ASCII letters. For example, "USD". Format is not restricted. Should
- * not be null.
- * @param userLocale User's current locale. Should not be null.
- */
- public CurrencyStringFormatter(String currencyCode, Locale userLocale) {
- assert currencyCode != null : "currencyCode should not be null";
- assert userLocale != null : "userLocale should not be null";
-
- mAmountValuePattern = Pattern.compile(AMOUNT_VALUE_PATTERN);
-
- mFormattedCurrencyCode = currencyCode.length() <= MAX_CURRENCY_CHARS
- ? currencyCode
- : currencyCode.substring(0, MAX_CURRENCY_CHARS - 1) + ELLIPSIS;
-
- String currencySymbol;
- int defaultFractionDigits;
- try {
- Currency currency = Currency.getInstance(currencyCode);
- currencySymbol = currency.getSymbol();
- defaultFractionDigits = currency.getDefaultFractionDigits();
- } catch (IllegalArgumentException e) {
- // The spec does not limit the currencies to official ISO 4217 currency code list, which
- // is used by java.util.Currency. For example, "BTX" (bitcoin) is not an official ISO
- // 4217 currency code, but is allowed by the spec.
- currencySymbol = "";
- defaultFractionDigits = 0;
- }
-
- // If the prefix of the currency symbol matches the prefix of the currency code, remove the
- // matching prefix from the symbol. The UI already shows the currency code, so there's no
- // need to show duplicate information.
- String symbol = "";
- for (int i = 0; i < currencySymbol.length(); i++) {
- if (i >= currencyCode.length() || currencySymbol.charAt(i) != currencyCode.charAt(i)) {
- symbol = currencySymbol.substring(i);
- break;
- }
- }
- mCurrencySymbol = symbol;
-
- mDefaultFractionDigits = defaultFractionDigits;
-
- // Use the symbols from user's current locale. For example, use "," for decimal separator in
- // France, even if paying in "USD".
- DecimalFormatSymbols symbols = new DecimalFormatSymbols(userLocale);
- mGroupingSeparator = symbols.getGroupingSeparator();
- mMonetaryDecimalSeparator = symbols.getMonetaryDecimalSeparator();
- }
-
- /**
- * Returns true if the amount value string is in valid format.
- *
- * @param amountValue The number to check for validity.
- * @return Whether the number is in valid format.
- */
- public boolean isValidAmountValue(String amountValue) {
- return amountValue != null && mAmountValuePattern.matcher(amountValue).matches();
- }
-
- /**
- * Returns true if the currency code string is in valid format.
- *
- * @param amountCurrencyCode The currency code to check for validity.
- * @return Whether the currency code is in valid format.
- */
- public boolean isValidAmountCurrencyCode(String amountCurrencyCode) {
- return amountCurrencyCode != null && amountCurrencyCode.length() <= MAX_CURRENCY_CODE_LEN;
- }
-
- /** @return The currency code formatted for display. */
- public String getFormattedCurrencyCode() {
- return mFormattedCurrencyCode;
- }
-
- /**
- * Formats the currency string for display. Does not parse the string into a number, because it
- * might be too large. The number is formatted for the current locale and follows the symbol of
- * the currency code.
- *
- * @param amountValue The number to format. Should be in "^-?[0-9]+(\.[0-9]+)?$" format. Should
- * not be null.
- * @return The currency symbol followed by a space and the formatted number.
- */
- public String format(String amountValue) {
- assert amountValue != null : "amountValue should not be null";
-
- Matcher m = mAmountValuePattern.matcher(amountValue);
-
- // Required to capture the groups.
- boolean matches = m.matches();
- assert matches;
-
- StringBuilder result = new StringBuilder(m.group(OPTIONAL_NEGATIVE_GROUP));
- result.append(mCurrencySymbol);
- int digitStart = result.length();
-
- result.append(m.group(DIGITS_BETWEEN_NEGATIVE_AND_PERIOD_GROUP));
- for (int i = result.length() - DIGIT_GROUPING_SIZE; i > digitStart;
- i -= DIGIT_GROUPING_SIZE) {
- result.insert(i, mGroupingSeparator);
- }
-
- String decimals = m.group(DIGITS_AFTER_PERIOD_GROUP);
- int numberOfDecimals = decimals == null ? 0 : decimals.length();
-
- if (numberOfDecimals > 0 || mDefaultFractionDigits > 0) {
- result.append(mMonetaryDecimalSeparator);
- if (null != decimals) result.append(decimals);
-
- for (int i = numberOfDecimals; i < mDefaultFractionDigits; i++) {
- result.append("0");
- }
- }
-
- return result.toString();
- }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
index 0070b15e..15e0acb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
@@ -266,7 +266,7 @@
private ContactEditor mContactEditor;
private boolean mHasRecordedAbortReason;
private boolean mQueriedCanMakePayment;
- private CurrencyStringFormatter mFormatter;
+ private CurrencyFormatter mCurrencyFormatter;
/** True if any of the requested payment methods are supported. */
private boolean mArePaymentMethodsSupported;
@@ -326,6 +326,14 @@
recordSuccessFunnelHistograms("Initiated");
}
+ protected void finalize() throws Throwable {
+ super.finalize();
+ if (mCurrencyFormatter != null) {
+ // Ensures the native implementation of currency formatter does not leak.
+ mCurrencyFormatter.destroy();
+ }
+ }
+
/**
* Called by the merchant website to initialize the payment request data.
*/
@@ -595,23 +603,23 @@
return false;
}
- if (mFormatter == null) {
- mFormatter = new CurrencyStringFormatter(details.total.amount.currency,
- Locale.getDefault());
+ if (mCurrencyFormatter == null) {
+ mCurrencyFormatter = new CurrencyFormatter(details.total.amount.currency,
+ details.total.amount.currencySystem, Locale.getDefault());
}
// Total is never pending.
- LineItem uiTotal = new LineItem(
- details.total.label, mFormatter.getFormattedCurrencyCode(),
- mFormatter.format(details.total.amount.value), /* isPending */ false);
+ LineItem uiTotal = new LineItem(details.total.label,
+ mCurrencyFormatter.getFormattedCurrencyCode(),
+ mCurrencyFormatter.format(details.total.amount.value), /* isPending */ false);
- List<LineItem> uiLineItems = getLineItems(details.displayItems, mFormatter);
+ List<LineItem> uiLineItems = getLineItems(details.displayItems, mCurrencyFormatter);
mUiShoppingCart = new ShoppingCart(uiTotal, uiLineItems);
mRawTotal = details.total;
mRawLineItems = Collections.unmodifiableList(Arrays.asList(details.displayItems));
- mUiShippingOptions = getShippingOptions(details.shippingOptions, mFormatter);
+ mUiShippingOptions = getShippingOptions(details.shippingOptions, mCurrencyFormatter);
for (int i = 0; i < details.modifiers.length; i++) {
PaymentDetailsModifier modifier = details.modifiers[i];
@@ -636,8 +644,9 @@
for (int i = 0; i < mPaymentMethodsSection.getSize(); i++) {
PaymentInstrument instrument = (PaymentInstrument) mPaymentMethodsSection.getItem(i);
PaymentDetailsModifier modifier = getModifier(instrument);
- instrument.setModifiedTotal(modifier == null || modifier.total == null ? null
- : mFormatter.format(modifier.total.amount.value));
+ instrument.setModifiedTotal(modifier == null || modifier.total == null
+ ? null
+ : mCurrencyFormatter.format(modifier.total.amount.value));
}
updateOrderSummary((PaymentInstrument) mPaymentMethodsSection.getSelectedItem());
@@ -651,10 +660,12 @@
PaymentItem total = modifier == null ? null : modifier.total;
if (total == null) total = mRawTotal;
- mUiShoppingCart.setTotal(new LineItem(total.label, mFormatter.getFormattedCurrencyCode(),
- mFormatter.format(total.amount.value), false /* isPending */));
- mUiShoppingCart.setAdditionalContents(modifier == null ? null
- : getLineItems(modifier.additionalDisplayItems, mFormatter));
+ mUiShoppingCart.setTotal(
+ new LineItem(total.label, mCurrencyFormatter.getFormattedCurrencyCode(),
+ mCurrencyFormatter.format(total.amount.value), false /* isPending */));
+ mUiShoppingCart.setAdditionalContents(modifier == null
+ ? null
+ : getLineItems(modifier.additionalDisplayItems, mCurrencyFormatter));
mUI.updateOrderSummarySection(mUiShoppingCart);
}
@@ -674,7 +685,7 @@
* @return A list of valid line items.
*/
private static List<LineItem> getLineItems(
- @Nullable PaymentItem[] items, CurrencyStringFormatter formatter) {
+ @Nullable PaymentItem[] items, CurrencyFormatter formatter) {
// Line items are optional.
if (items == null) return new ArrayList<>();
@@ -697,7 +708,7 @@
* @return The UI representation of the shipping options.
*/
private static SectionInformation getShippingOptions(
- @Nullable PaymentShippingOption[] options, CurrencyStringFormatter formatter) {
+ @Nullable PaymentShippingOption[] options, CurrencyFormatter formatter) {
// Shipping options are optional.
if (options == null || options.length == 0) {
return new SectionInformation(PaymentRequestUI.TYPE_SHIPPING_OPTIONS);
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 9d71001..4ea2d7e 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -692,7 +692,7 @@
"java/src/org/chromium/chrome/browser/payments/AutofillPaymentInstrument.java",
"java/src/org/chromium/chrome/browser/payments/CardEditor.java",
"java/src/org/chromium/chrome/browser/payments/ContactEditor.java",
- "java/src/org/chromium/chrome/browser/payments/CurrencyStringFormatter.java",
+ "java/src/org/chromium/chrome/browser/payments/CurrencyFormatter.java",
"java/src/org/chromium/chrome/browser/payments/EditorBase.java",
"java/src/org/chromium/chrome/browser/payments/PaymentApp.java",
"java/src/org/chromium/chrome/browser/payments/PaymentAppFactory.java",
@@ -1339,7 +1339,7 @@
"javatests/src/org/chromium/chrome/browser/physicalweb/UrlInfoTest.java",
"javatests/src/org/chromium/chrome/browser/physicalweb/UrlManagerTest.java",
"javatests/src/org/chromium/chrome/browser/policy/CombinedPolicyProviderTest.java",
- "javatests/src/org/chromium/chrome/browser/payments/CurrencyStringFormatterTest.java",
+ "javatests/src/org/chromium/chrome/browser/payments/CurrencyFormatterTest.java",
"javatests/src/org/chromium/chrome/browser/payments/PaymentRequestAbortTest.java",
"javatests/src/org/chromium/chrome/browser/payments/PaymentRequestBasicCardTest.java",
"javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCanMakePaymentQueryNoCardTest.java",
@@ -1536,7 +1536,6 @@
"junit/src/org/chromium/chrome/browser/omaha/VersionNumberTest.java",
"junit/src/org/chromium/chrome/browser/payments/AutofillContactTest.java",
"junit/src/org/chromium/chrome/browser/payments/AutofillContactUnitTest.java",
- "junit/src/org/chromium/chrome/browser/payments/CurrencyStringFormatterUnitTest.java",
"junit/src/org/chromium/chrome/browser/snackbar/SnackbarCollectionUnitTest.java",
"junit/src/org/chromium/chrome/browser/superviseduser/SupervisedUserContentProviderUnitTest.java",
"junit/src/org/chromium/chrome/browser/tabstate/TabStateUnitTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/CurrencyFormatterTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/CurrencyFormatterTest.java
new file mode 100644
index 0000000..4853fdd1
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/CurrencyFormatterTest.java
@@ -0,0 +1,113 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.payments;
+
+import android.support.test.filters.MediumTest;
+
+import junit.framework.Assert;
+
+import org.chromium.base.LocaleUtils;
+import org.chromium.content.browser.test.NativeLibraryTestBase;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * A lightweight integration test for CurrencyFormatter to run on an Android device.
+ */
+public class CurrencyFormatterTest extends NativeLibraryTestBase {
+ /**
+ * Unicode non-breaking space.
+ */
+ private static final String SPACE = "\u00A0";
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ loadNativeLibraryAndInitBrowserProcess();
+ }
+
+ private static String longStringOfLength(int len) {
+ StringBuilder currency = new StringBuilder();
+ for (int i = 0; i < len; i++) {
+ currency.append("A");
+ }
+ return currency.toString();
+ }
+
+ @MediumTest
+ public void testMultipleConversions() throws Exception {
+ // Note, all spaces are expected to be unicode non-breaking spaces. Here they are shown as
+ // normal spaces.
+ List<Object[]> testCases = Arrays.asList(new Object[][] {
+ {"55.00", "USD", "en-US", "USD", "$55.00"},
+ {"55.00", "USD", "en-CA", "USD", "$55.00"},
+ {"55.00", "USD", "fr-CA", "USD", "55,00 $"},
+ {"55.00", "USD", "fr-FR", "USD", "55,00 $"},
+ {"1234", "USD", "fr-FR", "USD", "1 234,00 $"},
+
+ {"55.5", "USD", "en-US", "USD", "$55.50"}, {"55", "USD", "en-US", "USD", "$55.00"},
+ {"-123", "USD", "en-US", "USD", "-$123.00"},
+ {"-1234", "USD", "en-US", "USD", "-$1,234.00"},
+ {"0.1234", "USD", "en-US", "USD", "$0.1234"},
+
+ {"55.00", "EUR", "en-US", "EUR", "€55.00"},
+ {"55.00", "EUR", "en-CA", "EUR", "€55.00"},
+ {"55.00", "EUR", "fr-CA", "EUR", "55,00 €"},
+ {"55.00", "EUR", "fr-FR", "EUR", "55,00 €"},
+
+ {"55.00", "CAD", "en-US", "CAD", "$55.00"},
+ {"55.00", "CAD", "en-CA", "CAD", "$55.00"},
+ {"55.00", "CAD", "fr-CA", "CAD", "55,00 $"},
+ {"55.00", "CAD", "fr-FR", "CAD", "55,00 $"},
+
+ {"55", "JPY", "ja-JP", "JPY", "¥55"}, {"55.0", "JPY", "ja-JP", "JPY", "¥55"},
+ {"55.00", "JPY", "ja-JP", "JPY", "¥55"},
+ {"55.12", "JPY", "ja-JP", "JPY", "¥55.12"},
+ {"55.49", "JPY", "ja-JP", "JPY", "¥55.49"},
+ {"55.50", "JPY", "ja-JP", "JPY", "¥55.5"},
+ {"55.9999", "JPY", "ja-JP", "JPY", "¥55.9999"},
+
+ // Unofficial ISO 4217 currency code.
+ {"55.00", "BTX", "en-US", "BTX", "55.00"},
+ {"-0.00000001", "BTX", "en-US", "BTX", "-0.00000001"},
+ {"-55.00", "BTX", "fr-FR", "BTX", "-55,00"},
+
+ {"123456789012345678901234567890.123456789012345678901234567890", "USD", "fr-FR",
+ "USD", "123 456 789 012 345 678 901 234 567 890,123456789 $"},
+ {"123456789012345678901234567890.123456789012345678901234567890", "USD", "en-US",
+ "USD", "$123,456,789,012,345,678,901,234,567,890.123456789"},
+
+ // Any string of at most 2048 characters can be valid amount currency codes.
+ {"55.00", "", "en-US", "", "55.00"},
+ {"55.00", "ABCDEF", "en-US", "ABCDE\u2026", "55.00"},
+ // Currency code more than 6 character is formatted to first 5 characters and
+ // ellipsis. "\u2026" is unicode for ellipsis.
+ {"55.00", longStringOfLength(2048), "en-US", "AAAAA\u2026", "55.00"},
+ });
+
+ for (int i = 0; i < testCases.size(); i++) {
+ Object[] testCase = testCases.get(i);
+
+ String amount = (String) testCase[0];
+ String currency = (String) testCase[1];
+ String locale = (String) testCase[2];
+ String expectedCurrencyFormatting = (String) testCase[3];
+ String expectedAmountFormatting = (String) testCase[4];
+
+ CurrencyFormatter formatter =
+ new CurrencyFormatter(currency, null, LocaleUtils.forLanguageTag(locale));
+
+ String formattedAmount = formatter.format(amount).replace(SPACE, " ");
+ Assert.assertEquals("\"" + currency + "\" \"" + amount + "\" (\"" + locale
+ + "\" locale) should be formatted into \"" + expectedAmountFormatting
+ + "\"",
+ expectedAmountFormatting, formattedAmount);
+ Assert.assertEquals("\"" + currency + "\""
+ + " should be formatted into \"" + expectedCurrencyFormatting + "\"",
+ expectedCurrencyFormatting, formatter.getFormattedCurrencyCode());
+ }
+ }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/CurrencyStringFormatterTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/CurrencyStringFormatterTest.java
deleted file mode 100644
index 810a0ae..0000000
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/CurrencyStringFormatterTest.java
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.payments;
-
-import android.support.test.filters.SmallTest;
-import android.test.InstrumentationTestCase;
-
-import java.util.Locale;
-
-/**
- * A lightweight integration test for CurrencyStringFormatter to run on an Android device.
- */
-public class CurrencyStringFormatterTest extends InstrumentationTestCase {
- @SmallTest
- public void testCad() throws Exception {
- CurrencyStringFormatter formatter = new CurrencyStringFormatter("CAD", new Locale("en-US"));
- assertEquals("$5.00", formatter.format("5"));
- }
-
- @SmallTest
- public void testAzn() throws Exception {
- CurrencyStringFormatter formatter = new CurrencyStringFormatter("AZN", new Locale("en-US"));
- assertEquals("5.00", formatter.format("5"));
- }
-
- @SmallTest
- public void testBmd() throws Exception {
- CurrencyStringFormatter formatter = new CurrencyStringFormatter("BMD", new Locale("en-US"));
- assertEquals("5.00", formatter.format("5"));
- }
-
- @SmallTest
- public void testAud() throws Exception {
- CurrencyStringFormatter formatter = new CurrencyStringFormatter("AUD", new Locale("en-US"));
- assertEquals("$5.00", formatter.format("5"));
- }
-
- @SmallTest
- public void testBzd() throws Exception {
- CurrencyStringFormatter formatter = new CurrencyStringFormatter("BZD", new Locale("en-US"));
- assertEquals("5.00", formatter.format("5"));
- }
-
- @SmallTest
- public void testClp() throws Exception {
- CurrencyStringFormatter formatter = new CurrencyStringFormatter("CLP", new Locale("en-US"));
- assertEquals("5", formatter.format("5"));
- }
-
- @SmallTest
- public void testLrd() throws Exception {
- CurrencyStringFormatter formatter = new CurrencyStringFormatter("LRD", new Locale("en-US"));
- assertEquals("5.00", formatter.format("5"));
- }
-
- @SmallTest
- public void testNio() throws Exception {
- CurrencyStringFormatter formatter = new CurrencyStringFormatter("NIO", new Locale("en-US"));
- assertEquals("5.00", formatter.format("5"));
- }
-
- @SmallTest
- public void testHrk() throws Exception {
- CurrencyStringFormatter formatter = new CurrencyStringFormatter("HRK", new Locale("en-US"));
- assertEquals("5.00", formatter.format("5"));
- }
-
- @SmallTest
- public void testRub() throws Exception {
- CurrencyStringFormatter formatter = new CurrencyStringFormatter("RUB", new Locale("en-US"));
- assertEquals("5.00", formatter.format("5"));
- }
-
- @SmallTest
- public void testEur() throws Exception {
- CurrencyStringFormatter formatter = new CurrencyStringFormatter("EUR", new Locale("en-US"));
- assertEquals("€5.00", formatter.format("5"));
- }
-
- @SmallTest
- public void testPkr() throws Exception {
- CurrencyStringFormatter formatter = new CurrencyStringFormatter("PKR", new Locale("en-US"));
- assertEquals("5", formatter.format("5"));
- }
-
- @SmallTest
- public void testCurrency6Chars() throws Exception {
- CurrencyStringFormatter formatter =
- new CurrencyStringFormatter("ABCDEF", new Locale("en-US"));
- assertEquals("ABCDEF", formatter.getFormattedCurrencyCode());
- }
-
- @SmallTest
- public void testCurrencyEllipsized() throws Exception {
- CurrencyStringFormatter formatter =
- new CurrencyStringFormatter("ABCDEFG", new Locale("en-US"));
- // Currency code more than 6 character is formatted to first 5 characters and ellipsis.
- // "\u2026" is unicode for ellipsis.
- assertEquals("ABCDE\u2026", formatter.getFormattedCurrencyCode());
- }
-
- @SmallTest
- public void testCurrencyEmpty() throws Exception {
- CurrencyStringFormatter formatter = new CurrencyStringFormatter("", new Locale("en-US"));
- assertEquals("", formatter.getFormattedCurrencyCode());
- }
-}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/payments/CurrencyStringFormatterUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/payments/CurrencyStringFormatterUnitTest.java
deleted file mode 100644
index 7a5e115b..0000000
--- a/chrome/android/junit/src/org/chromium/chrome/browser/payments/CurrencyStringFormatterUnitTest.java
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.payments;
-
-import org.junit.Assert;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Locale;
-
-/**
- * Unit tests for the CurrencyStringFormatter class.
- */
-@RunWith(Parameterized.class)
-public class CurrencyStringFormatterUnitTest {
- /**
- * Unicode non-breaking space.
- */
- private static final String SPACE = "\u00A0";
-
- private enum ExpectedValidity {
- VALID_AMOUNT,
- INVALID_AMOUNT_CURRENCY_CODE,
- INVALID_AMOUNT_VALUE,
- };
-
- @Parameters
- public static Collection<Object[]> data() {
- return Arrays.asList(new Object[][] {
- {"55.00", "USD", "en-US", "USD", "$55.00", ExpectedValidity.VALID_AMOUNT},
- {"55.00", "USD", "en-CA", "USD", "$55.00", ExpectedValidity.VALID_AMOUNT},
- {"55.00", "USD", "fr-CA", "USD", "$55,00", ExpectedValidity.VALID_AMOUNT},
- {"55.00", "USD", "fr-FR", "USD", "$55,00", ExpectedValidity.VALID_AMOUNT},
-
- {"55.00", "EUR", "en-US", "EUR", "55.00", ExpectedValidity.VALID_AMOUNT},
- {"55.00", "EUR", "en-CA", "EUR", "55.00", ExpectedValidity.VALID_AMOUNT},
- {"55.00", "EUR", "fr-CA", "EUR", "55,00", ExpectedValidity.VALID_AMOUNT},
- {"55.00", "EUR", "fr-FR", "EUR", "55,00", ExpectedValidity.VALID_AMOUNT},
-
- {"55.00", "CAD", "en-US", "CAD", "55.00", ExpectedValidity.VALID_AMOUNT},
- {"55.00", "CAD", "en-CA", "CAD", "55.00", ExpectedValidity.VALID_AMOUNT},
- {"55.00", "CAD", "fr-CA", "CAD", "55,00", ExpectedValidity.VALID_AMOUNT},
- {"55.00", "CAD", "fr-FR", "CAD", "55,00", ExpectedValidity.VALID_AMOUNT},
-
- {"55.12", "JPY", "ja-JP", "JPY", "55.12", ExpectedValidity.VALID_AMOUNT},
- {"55.00", "JPY", "ja-JP", "JPY", "55.00", ExpectedValidity.VALID_AMOUNT},
- {"55.0", "JPY", "ja-JP", "JPY", "55.0", ExpectedValidity.VALID_AMOUNT},
- {"55", "JPY", "ja-JP", "JPY", "55", ExpectedValidity.VALID_AMOUNT},
-
- // Unofficial ISO 4217 currency code.
- {"55.00", "BTX", "en-US", "BTX", "55.00", ExpectedValidity.VALID_AMOUNT},
- {"-55.00", "BTX", "en-US", "BTX", "-55.00", ExpectedValidity.VALID_AMOUNT},
-
- {"55.5", "USD", "en-US", "USD", "$55.50", ExpectedValidity.VALID_AMOUNT},
- {"55", "USD", "en-US", "USD", "$55.00", ExpectedValidity.VALID_AMOUNT},
- {"123", "USD", "en-US", "USD", "$123.00", ExpectedValidity.VALID_AMOUNT},
- {"1234", "USD", "en-US", "USD", "$1,234.00", ExpectedValidity.VALID_AMOUNT},
-
- {"-123", "USD", "en-US", "USD", "-$123.00", ExpectedValidity.VALID_AMOUNT},
- {"-1234", "USD", "en-US", "USD", "-$1,234.00", ExpectedValidity.VALID_AMOUNT},
-
- {"123456789012345678901234567890.123456789012345678901234567890", "USD", "fr-FR", "USD",
- "$123" + SPACE + "456" + SPACE + "789" + SPACE + "012" + SPACE + "345"
- + SPACE + "678" + SPACE + "901" + SPACE + "234" + SPACE + "567"
- + SPACE + "890,123456789012345678901234567890",
- ExpectedValidity.VALID_AMOUNT},
- {"123456789012345678901234567890.123456789012345678901234567890", "USD", "en-US", "USD",
- "$123,456,789,012,345,678,901,234,567,890.123456789012345678901234567890",
- ExpectedValidity.VALID_AMOUNT},
-
- // Any string of at most 2048 characters can be valid amount currency codes.
- {"55.00", "", "en-US", "", "55.00", ExpectedValidity.VALID_AMOUNT},
- {"55.00", "ABCDEF", "en-US", "ABCDEF", "55.00", ExpectedValidity.VALID_AMOUNT},
- // Currency code more than 6 character is formatted to first 5 characters and ellipsis.
- // "\u2026" is unicode for ellipsis.
- {"55.00", longStringOfLength(2048), "en-US", "AAAAA\u2026", "55.00",
- ExpectedValidity.VALID_AMOUNT},
-
- // Invalid amount currency codes.
- {"55.00", longStringOfLength(2049), "en-US", null, null,
- ExpectedValidity.INVALID_AMOUNT_CURRENCY_CODE},
-
- // Invalid amount values.
- {"", "USD", "en-US", "USD", null, ExpectedValidity.INVALID_AMOUNT_VALUE},
- {"-", "USD", "en-US", "USD", null, ExpectedValidity.INVALID_AMOUNT_VALUE},
- {"notdigits", "USD", "en-US", "USD", null, ExpectedValidity.INVALID_AMOUNT_VALUE},
- {"ALSONOTDIGITS", "USD", "en-US", "USD", null, ExpectedValidity.INVALID_AMOUNT_VALUE},
- {"10.", "USD", "en-US", "USD", null, ExpectedValidity.INVALID_AMOUNT_VALUE},
- {".99", "USD", "en-US", "USD", null, ExpectedValidity.INVALID_AMOUNT_VALUE},
- {"10-", "USD", "en-US", "USD", null, ExpectedValidity.INVALID_AMOUNT_VALUE},
- {"1-0", "USD", "en-US", "USD", null, ExpectedValidity.INVALID_AMOUNT_VALUE},
- {"1.0.0", "USD", "en-US", "USD", null, ExpectedValidity.INVALID_AMOUNT_VALUE},
- {"1/3", "USD", "en-US", "USD", null, ExpectedValidity.INVALID_AMOUNT_VALUE},
- });
- }
-
- private final String mAmount;
- private final String mCurrency;
- private final String mLanguageTag;
- private final String mExpectedCurrencyFormatting;
- private final String mExpectedAmountFormatting;
- private final ExpectedValidity mExpectedValidity;
-
- private static String longStringOfLength(int len) {
- StringBuilder currency = new StringBuilder();
- for (int i = 0; i < len; i++) {
- currency.append("A");
- }
- return currency.toString();
- }
-
- public CurrencyStringFormatterUnitTest(String amount, String currency, String languageTag,
- String expectedCurrencyFormatting, String expectedAmountFormatting,
- ExpectedValidity expectedValidity) {
- mAmount = amount;
- mCurrency = currency;
- mLanguageTag = languageTag;
- mExpectedCurrencyFormatting = expectedCurrencyFormatting;
- mExpectedAmountFormatting = expectedAmountFormatting;
- mExpectedValidity = expectedValidity;
- }
-
- @Test
- public void test() {
- CurrencyStringFormatter formatter = new CurrencyStringFormatter(mCurrency,
- Locale.forLanguageTag(mLanguageTag));
-
- if (mExpectedValidity == ExpectedValidity.INVALID_AMOUNT_CURRENCY_CODE) {
- Assert.assertFalse("\"" + mCurrency + "\" should be invalid currency code",
- formatter.isValidAmountCurrencyCode(mCurrency));
- } else {
- Assert.assertTrue("\"" + mCurrency + "\" should be valid currency code",
- formatter.isValidAmountCurrencyCode(mCurrency));
- }
-
- if (mExpectedValidity == ExpectedValidity.INVALID_AMOUNT_VALUE) {
- Assert.assertFalse("\"" + mAmount + "\" should be invalid amount value",
- formatter.isValidAmountValue(mAmount));
- } else {
- Assert.assertTrue("\"" + mAmount + "\" should be valid amount value",
- formatter.isValidAmountValue(mAmount));
- }
-
- if (mExpectedValidity == ExpectedValidity.VALID_AMOUNT) {
- Assert.assertEquals("\"" + mCurrency + "\" \"" + mAmount + "\" (\"" + mLanguageTag
- + "\" locale) should be formatted into \""
- + mExpectedAmountFormatting + "\"",
- mExpectedAmountFormatting, formatter.format(mAmount));
- Assert.assertEquals("\"" + mCurrency + "\"" + " should be formatted into \""
- + mExpectedCurrencyFormatting + "\"",
- mExpectedCurrencyFormatting, formatter.getFormattedCurrencyCode());
- }
- }
-}
diff --git a/chrome/browser/android/chrome_jni_registrar.cc b/chrome/browser/android/chrome_jni_registrar.cc
index b9f3baa..06161d3 100644
--- a/chrome/browser/android/chrome_jni_registrar.cc
+++ b/chrome/browser/android/chrome_jni_registrar.cc
@@ -168,6 +168,7 @@
#include "components/gcm_driver/android/component_jni_registrar.h"
#include "components/gcm_driver/instance_id/android/component_jni_registrar.h"
#include "components/invalidation/impl/android/component_jni_registrar.h"
+#include "components/payments/android/currency_formatter_android.h"
#include "components/payments/android/payments_jni_registrar.h"
#include "components/policy/core/browser/android/component_jni_registrar.h"
#include "components/safe_browsing_db/android/jni_registrar.h"
@@ -271,6 +272,7 @@
{"CreditCardScannerBridge",
autofill::CreditCardScannerViewAndroid::Register},
{"CtrSuppression", RegisterCtrSuppression},
+ {"CurrencyFormatter", payments::CurrencyFormatterAndroid::Register},
{"DataReductionPromoInfoBarDelegate",
DataReductionPromoInfoBarDelegateAndroid::Register},
{"DataReductionProxySettings", DataReductionProxySettingsAndroid::Register},
diff --git a/components/payments/android/BUILD.gn b/components/payments/android/BUILD.gn
index 01ccece..a1949e5 100644
--- a/components/payments/android/BUILD.gn
+++ b/components/payments/android/BUILD.gn
@@ -7,10 +7,13 @@
static_library("payments_jni") {
sources = [
+ "currency_formatter_android.cc",
+ "currency_formatter_android.h",
"payments_jni_registrar.cc",
]
deps = [
":jni_headers",
+ "//base",
"//components/payments:payment_request",
"//components/payments:payment_validation",
]
@@ -18,6 +21,7 @@
generate_jni("jni_headers") {
sources = [
+ "//chrome/android/java/src/org/chromium/chrome/browser/payments/CurrencyFormatter.java",
"//chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentValidator.java",
]
jni_package = "payments"
diff --git a/components/payments/android/currency_formatter_android.cc b/components/payments/android/currency_formatter_android.cc
new file mode 100644
index 0000000..00143f3
--- /dev/null
+++ b/components/payments/android/currency_formatter_android.cc
@@ -0,0 +1,76 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/payments/android/currency_formatter_android.h"
+
+#include "base/android/jni_string.h"
+#include "base/optional.h"
+#include "base/strings/string16.h"
+#include "components/payments/currency_formatter.h"
+#include "jni/CurrencyFormatter_jni.h"
+
+using base::android::JavaParamRef;
+using base::android::ConvertJavaStringToUTF8;
+
+namespace payments {
+
+CurrencyFormatterAndroid::CurrencyFormatterAndroid(
+ JNIEnv* env,
+ jobject unused_obj,
+ const JavaParamRef<jstring>& currency_code,
+ const JavaParamRef<jstring>& currency_system,
+ const JavaParamRef<jstring>& locale_name) {
+ std::string currency_system_str =
+ ConvertJavaStringToUTF8(env, currency_system);
+
+ currency_formatter_.reset(new CurrencyFormatter(
+ ConvertJavaStringToUTF8(env, currency_code),
+ currency_system_str.empty()
+ ? base::Optional<std::string>()
+ : base::Optional<std::string>(currency_system_str),
+ ConvertJavaStringToUTF8(env, locale_name)));
+}
+
+CurrencyFormatterAndroid::~CurrencyFormatterAndroid() {}
+
+void CurrencyFormatterAndroid::Destroy(JNIEnv* env,
+ const JavaParamRef<jobject>& obj) {
+ delete this;
+}
+
+base::android::ScopedJavaLocalRef<jstring> CurrencyFormatterAndroid::Format(
+ JNIEnv* env,
+ const JavaParamRef<jobject>& unused_obj,
+ const JavaParamRef<jstring>& amount) {
+ base::string16 result =
+ currency_formatter_->Format(ConvertJavaStringToUTF8(env, amount));
+ return base::android::ConvertUTF16ToJavaString(env, result);
+}
+
+base::android::ScopedJavaLocalRef<jstring>
+CurrencyFormatterAndroid::GetFormattedCurrencyCode(
+ JNIEnv* env,
+ const JavaParamRef<jobject>& unused_obj) {
+ return base::android::ConvertUTF8ToJavaString(
+ env, currency_formatter_->formatted_currency_code());
+}
+
+// static
+bool CurrencyFormatterAndroid::Register(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+static jlong InitCurrencyFormatterAndroid(
+ JNIEnv* env,
+ const JavaParamRef<jobject>& obj,
+ const JavaParamRef<jstring>& currency_code,
+ const JavaParamRef<jstring>& currency_system,
+ const JavaParamRef<jstring>& locale_name) {
+ CurrencyFormatterAndroid* currency_formatter_android =
+ new CurrencyFormatterAndroid(env, obj, currency_code, currency_system,
+ locale_name);
+ return reinterpret_cast<intptr_t>(currency_formatter_android);
+}
+
+} // namespace payments
diff --git a/components/payments/android/currency_formatter_android.h b/components/payments/android/currency_formatter_android.h
new file mode 100644
index 0000000..f6e6b33e2
--- /dev/null
+++ b/components/payments/android/currency_formatter_android.h
@@ -0,0 +1,51 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PAYMENTS_ANDROID_CURRENCY_FORMATTER_ANDROID_H_
+#define COMPONENTS_PAYMENTS_ANDROID_CURRENCY_FORMATTER_ANDROID_H_
+
+#include <memory>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/macros.h"
+#include "components/payments/currency_formatter.h"
+
+namespace payments {
+
+// Forwarding calls to payments::CurrencyFormatter.
+class CurrencyFormatterAndroid {
+ public:
+ CurrencyFormatterAndroid(
+ JNIEnv* env,
+ jobject unused_obj,
+ const base::android::JavaParamRef<jstring>& currency_code,
+ const base::android::JavaParamRef<jstring>& currency_system,
+ const base::android::JavaParamRef<jstring>& locale_name);
+ ~CurrencyFormatterAndroid();
+
+ // Message from Java to destroy this object.
+ void Destroy(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj);
+
+ // Refer to CurrencyFormatter::Format documentation.
+ base::android::ScopedJavaLocalRef<jstring> Format(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& unused_obj,
+ const base::android::JavaParamRef<jstring>& amount);
+
+ base::android::ScopedJavaLocalRef<jstring> GetFormattedCurrencyCode(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& unused_obj);
+
+ // Registers the JNI bindings for this class.
+ static bool Register(JNIEnv* env);
+
+ private:
+ std::unique_ptr<CurrencyFormatter> currency_formatter_;
+
+ DISALLOW_COPY_AND_ASSIGN(CurrencyFormatterAndroid);
+};
+
+} // namespace payments
+
+#endif // COMPONENTS_PAYMENTS_ANDROID_CURRENCY_FORMATTER_ANDROID_H_
diff --git a/components/payments/currency_formatter.cc b/components/payments/currency_formatter.cc
index f333787..d6d4bc0 100644
--- a/components/payments/currency_formatter.cc
+++ b/components/payments/currency_formatter.cc
@@ -17,6 +17,7 @@
namespace payments {
const char kIso4217CurrencySystem[] = "urn:iso:std:iso:4217";
+
namespace {
// Support a maximum of 10 fractional digits, similar to the ISO20022 standard.
@@ -27,6 +28,12 @@
// Max currency code length. Length of currency code can be at most 2048.
const static size_t kMaxCurrencyCodeLength = 2048;
+// Currency codes longer than 6 characters get truncated to 5 + ellipsis.
+const static size_t kMaxCurrencyCodeDisplayedChars = 6;
+
+// Used to truncate long currency codes.
+const char kEllipsis[] = "\xE2\x80\xA6";
+
// Returns whether the |currency_code| is valid to be used in ICU.
bool ShouldUseCurrencyCode(const std::string& currency_code,
const base::Optional<std::string> currency_system) {
@@ -36,22 +43,27 @@
currency_code.size() <= kMaxCurrencyCodeLength;
}
+std::string FormatCurrencyCode(const std::string& currency_code) {
+ return currency_code.length() < kMaxCurrencyCodeDisplayedChars
+ ? currency_code
+ : currency_code.substr(0, kMaxCurrencyCodeDisplayedChars - 1) +
+ kEllipsis;
+}
+
} // namespace
CurrencyFormatter::CurrencyFormatter(
const std::string& currency_code,
const base::Optional<std::string> currency_system,
const std::string& locale_name)
- : locale_(locale_name.c_str()) {
+ : locale_(locale_name.c_str()),
+ formatted_currency_code_(FormatCurrencyCode(currency_code)) {
UErrorCode error_code = U_ZERO_ERROR;
icu_formatter_.reset(
icu::NumberFormat::createCurrencyInstance(locale_, error_code));
if (U_FAILURE(error_code)) {
- icu::UnicodeString name;
- std::string locale_str;
- locale_.getDisplayName(name).toUTF8String(locale_str);
LOG(ERROR) << "Failed to initialize the currency formatter for "
- << locale_str;
+ << locale_name;
return;
}
diff --git a/components/payments/currency_formatter.h b/components/payments/currency_formatter.h
index 06695a84..2794b37 100644
--- a/components/payments/currency_formatter.h
+++ b/components/payments/currency_formatter.h
@@ -41,9 +41,14 @@
// formatter, this method will return |amount|.
base::string16 Format(const std::string& amount);
+ // Returns the formatted currency code (<= 6 characters including ellipsis if
+ // applicable).
+ std::string formatted_currency_code() { return formatted_currency_code_; }
+
private:
const icu::Locale locale_;
std::unique_ptr<icu::UnicodeString> currency_code_;
+ std::string formatted_currency_code_;
std::unique_ptr<icu::NumberFormat> icu_formatter_;
DISALLOW_COPY_AND_ASSIGN(CurrencyFormatter);
diff --git a/components/payments/currency_formatter_unittest.cc b/components/payments/currency_formatter_unittest.cc
index 02e6cfb7..ad679c62 100644
--- a/components/payments/currency_formatter_unittest.cc
+++ b/components/payments/currency_formatter_unittest.cc
@@ -17,11 +17,13 @@
const char* currency_code,
const char* locale_name,
const std::string& expected_amount,
+ const char* expected_currency_code,
const char* currency_system = kIso4217CurrencySystem)
: amount(amount),
currency_code(currency_code),
locale_name(locale_name),
expected_amount(expected_amount),
+ expected_currency_code(expected_currency_code),
currency_system(currency_system) {}
~TestCase() {}
@@ -29,6 +31,7 @@
const char* const currency_code;
const char* const locale_name;
const std::string expected_amount;
+ const char* const expected_currency_code;
const base::Optional<std::string> currency_system;
};
@@ -51,82 +54,125 @@
EXPECT_EQ(converted, output_amount)
<< "Failed to convert " << GetParam().amount << " ("
<< GetParam().currency_code << ") in " << GetParam().locale_name;
+ EXPECT_EQ(GetParam().expected_currency_code,
+ formatter.formatted_currency_code());
}
INSTANTIATE_TEST_CASE_P(
CurrencyAmounts,
PaymentsCurrencyFormatterTest,
testing::Values(
- TestCase("55.00", "USD", "en_US", "$55.00"),
- TestCase("55.00", "USD", "en_CA", "$55.00"),
- TestCase("55.00", "USD", "fr_CA", "55,00 $"),
- TestCase("55.00", "USD", "fr_FR", "55,00 $"),
- TestCase("1234", "USD", "fr_FR", "1 234,00 $"),
+ TestCase("55.00", "USD", "en_US", "$55.00", "USD"),
+ TestCase("55.00", "USD", "en_CA", "$55.00", "USD"),
+ TestCase("55.00", "USD", "fr_CA", "55,00 $", "USD"),
+ TestCase("55.00", "USD", "fr_FR", "55,00 $", "USD"),
+ TestCase("1234", "USD", "fr_FR", "1 234,00 $", "USD"),
- TestCase("55.5", "USD", "en_US", "$55.50"),
- TestCase("55", "USD", "en_US", "$55.00"),
- TestCase("123", "USD", "en_US", "$123.00"),
- TestCase("1234", "USD", "en_US", "$1,234.00"),
- TestCase("0.1234", "USD", "en_US", "$0.1234"),
+ TestCase("55.5", "USD", "en_US", "$55.50", "USD"),
+ TestCase("55", "USD", "en_US", "$55.00", "USD"),
+ TestCase("123", "USD", "en_US", "$123.00", "USD"),
+ TestCase("1234", "USD", "en_US", "$1,234.00", "USD"),
+ TestCase("0.1234", "USD", "en_US", "$0.1234", "USD"),
- TestCase("55.00", "EUR", "en_US", "€55.00"),
- TestCase("55.00", "EUR", "fr_CA", "55,00 €"),
- TestCase("55.00", "EUR", "fr_FR", "55,00 €"),
+ TestCase("55.00", "EUR", "en_US", "€55.00", "EUR"),
+ TestCase("55.00", "EUR", "fr_CA", "55,00 €", "EUR"),
+ TestCase("55.00", "EUR", "fr_FR", "55,00 €", "EUR"),
- TestCase("55.00", "CAD", "en_US", "$55.00"),
- TestCase("55.00", "CAD", "en_CA", "$55.00"),
- TestCase("55.00", "CAD", "fr_CA", "55,00 $"),
- TestCase("55.00", "CAD", "fr_FR", "55,00 $"),
+ TestCase("55.00", "CAD", "en_US", "$55.00", "CAD"),
+ TestCase("55.00", "CAD", "en_CA", "$55.00", "CAD"),
+ TestCase("55.00", "CAD", "fr_CA", "55,00 $", "CAD"),
+ TestCase("55.00", "CAD", "fr_FR", "55,00 $", "CAD"),
- TestCase("55.00", "BRL", "en_US", "R$55.00"),
- TestCase("55.00", "BRL", "fr_CA", "55,00 R$"),
- TestCase("55.00", "BRL", "pt_BR", "R$55,00"),
+ TestCase("55.00", "BRL", "en_US", "R$55.00", "BRL"),
+ TestCase("55.00", "BRL", "fr_CA", "55,00 R$", "BRL"),
+ TestCase("55.00", "BRL", "pt_BR", "R$55,00", "BRL"),
- TestCase("55.00", "RUB", "en_US", "55.00"),
- TestCase("55.00", "RUB", "fr_CA", "55,00"),
- TestCase("55.00", "RUB", "ru_RU", "55,00 ₽"),
+ TestCase("55.00", "RUB", "en_US", "55.00", "RUB"),
+ TestCase("55.00", "RUB", "fr_CA", "55,00", "RUB"),
+ TestCase("55.00", "RUB", "ru_RU", "55,00 ₽", "RUB"),
- TestCase("55", "JPY", "ja_JP", "¥55"),
- TestCase("55.0", "JPY", "ja_JP", "¥55"),
- TestCase("55.00", "JPY", "ja_JP", "¥55"),
- TestCase("55.12", "JPY", "ja_JP", "¥55.12"),
- TestCase("55.49", "JPY", "ja_JP", "¥55.49"),
- TestCase("55.50", "JPY", "ja_JP", "¥55.5"),
- TestCase("55.9999", "JPY", "ja_JP", "¥55.9999"),
+ TestCase("55", "JPY", "ja_JP", "¥55", "JPY"),
+ TestCase("55.0", "JPY", "ja_JP", "¥55", "JPY"),
+ TestCase("55.00", "JPY", "ja_JP", "¥55", "JPY"),
+ TestCase("55.12", "JPY", "ja_JP", "¥55.12", "JPY"),
+ TestCase("55.49", "JPY", "ja_JP", "¥55.49", "JPY"),
+ TestCase("55.50", "JPY", "ja_JP", "¥55.5", "JPY"),
+ TestCase("55.9999", "JPY", "ja_JP", "¥55.9999", "JPY"),
// Unofficial ISO 4217 currency code.
- TestCase("55.00", "BTC", "en_US", "55.00"),
- TestCase("-0.0000000001", "BTC", "en_US", "-0.0000000001"),
- TestCase("-55.00", "BTC", "fr_FR", "-55,00"),
+ TestCase("55.00", "BTC", "en_US", "55.00", "BTC"),
+ TestCase("-0.0000000001", "BTC", "en_US", "-0.0000000001", "BTC"),
+ TestCase("-55.00", "BTC", "fr_FR", "-55,00", "BTC"),
// Any string of at most 2048 characters can be a valid currency code.
- TestCase("55.00", "", "en_US", "55.00"),
- TestCase("55,00", "", "fr_CA", "55,00"),
- TestCase("55,00", "", "fr-CA", "55,00"),
- TestCase("55.00", "ABCDEF", "en_US", "55.00"),
+ TestCase("55.00", "", "en_US", "55.00", ""),
+ TestCase("55,00", "", "fr_CA", "55,00", ""),
+ TestCase("55,00", "", "fr-CA", "55,00", ""),
+ TestCase("55.00", "ABCDEF", "en_US", "55.00", "ABCDE\xE2\x80\xA6"),
// Edge cases.
- TestCase("", "", "", ""),
- TestCase("-1", "", "", "- 1.00"),
- TestCase("-1.1255", "", "", "- 1.1255"),
+ TestCase("", "", "", "", ""),
+ TestCase("-1", "", "", "- 1.00", ""),
+ TestCase("-1.1255", "", "", "- 1.1255", ""),
// Handles big numbers.
TestCase(
"123456789012345678901234567890.123456789012345678901234567890",
"USD",
"fr_FR",
- "123 456 789 012 345 678 901 234 567 890,123456789 $"),
+ "123 456 789 012 345 678 901 234 567 890,123456789 $",
+ "USD"),
// When the currency system is not ISO4217, only the amount is formatted
// using the locale (there is no other indication of currency).
- TestCase("55.00", "USD", "en_CA", "55.00", "http://currsystem.com"),
- TestCase("55.00", "USD", "fr_CA", "55,00", "http://currsystem.com"),
- TestCase("55.00", "USD", "fr_FR", "55,00", "http://currsystem.com"),
- TestCase("1234", "USD", "fr_FR", "1 234,00", "http://currsystem.com"),
- TestCase("55.5", "USD", "en_US", "55.50", "http://currsystem.com"),
- TestCase("55", "CAD", "en_US", "55.00", "http://currsystem.com"),
- TestCase("123", "BTC", "en_US", "123.00", "http://currsystem.com"),
- TestCase("1234", "JPY", "en_US", "1,234.00", "http://currsystem.com"),
- TestCase("0.1234", "USD", "en_US", "0.1234", "http://currsystem.com")));
+ TestCase("55.00",
+ "USD",
+ "en_CA",
+ "55.00",
+ "USD",
+ "http://currsystem.com"),
+ TestCase("55.00",
+ "USD",
+ "fr_CA",
+ "55,00",
+ "USD",
+ "http://currsystem.com"),
+ TestCase("55.00",
+ "USD",
+ "fr_FR",
+ "55,00",
+ "USD",
+ "http://currsystem.com"),
+ TestCase("1234",
+ "USD",
+ "fr_FR",
+ "1 234,00",
+ "USD",
+ "http://currsystem.com"),
+ TestCase("55.5",
+ "USD",
+ "en_US",
+ "55.50",
+ "USD",
+ "http://currsystem.com"),
+ TestCase("55", "CAD", "en_US", "55.00", "CAD", "http://currsystem.com"),
+ TestCase("123",
+ "BTC",
+ "en_US",
+ "123.00",
+ "BTC",
+ "http://currsystem.com"),
+ TestCase("1234",
+ "JPY",
+ "en_US",
+ "1,234.00",
+ "JPY",
+ "http://currsystem.com"),
+ TestCase("0.1234",
+ "USD",
+ "en_US",
+ "0.1234",
+ "USD",
+ "http://currsystem.com")));
} // namespace payments