The ability of our software products to function correctly in a global environment is becoming more and more important. Our software should support national conventions used by the various locales around the globe. For example, in some regions of the world the period character is used as the number group separator and the comma is used as the decimal symbol (radix). European calendars generally start on Monday rather than Sunday which is customary in the United States. Era based calendars are still in common use in Japan and Korea, date formats and order, and time formats also vary by region or locale. As testers we need to test our software to ensure our customers around the world can use the national conventions they are accustomed to, and not force them down a US-centric, one-size-fits-all format or standard.
There are several settings that we can modify and customize for more robust globalization testing such as number, currency, time and date formats. Modifying these settings can help us test that our application is globalized to use National Language System (NLS) APIs provided by the system.Although a user would change these settings using the Regional Options user interface property sheets, if the purpose of our test is not to emulate user interaction, then modifying the custom regional settings for globalization testing programmatically is more efficient.
Last year I talked about how to programmatically make changes to the settings in the Region and Language control panel applet when doing globalization testing. Unfortunately, the code sample provided in the previous post was appropriate for versions of Windows XP and earlier. For versions of Windows Vista and later things have changed a bit. Also, the previous sample tried to be a one-size fits all and relied on the test developer to set the appropriate lcType constants and lcData argument variables required by the Win32 function SetLocaleInfo().
This time, I decided to simplify things a bit and wrapped some methods to call the appropriate Win32 API functions and properties to set lcType and lcData values to make it easier to incorporate into automated tests. I also separated the various advanced custom formats for Region and Language options into separate classes. Of course, I have a beta version of an automation library (DLL) called GlobalTest.DLL on my website that testers can use in their automated test cases, but this week let’s look at the class for setting custom date formats.
Making these changes programmatically still requires the Win32 SetLocaleInfo() function. MSDN also states this function modifies the specified values for all applications, so to prevent potential issues in other applications running on the system we should also broadcast the WM_SETTINGCHANGE message. To broadcast the WM_SETTINGCHANGE message we will also need the Win32 PostMessage() function. Since we are Process Invocation (PInvoke) to call these unmanaged functions we should put them in a separate class that I’ve called NativeMethods. I also included all necessary constant values required by these methods in the NativeMethods class also as illustrated below.
1: namespace TestingMentor.TestTool.GlobalTester
2: {
3: using System;
4: using System.Runtime.InteropServices;
5:
6: internal sealed class NativeMethods
7: {
8: internal const int SystemDefaultLocale = (int)0x00000800;
9: internal const int BroadcastMessage = (int)0x0000FFFF;
10: internal const int SettingChangeMessage = (int)0x0000001A;
11:
12: private NativeMethods() { }
13:
14: [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
15: [return: MarshalAs(UnmanagedType.Bool)]
16: internal static extern bool SetLocaleInfo(
17: int locale,
18: int localeType,
19: string localeData);
20:
21: [DllImport("user32.dll", SetLastError = true)]
22: [return: MarshalAs(UnmanagedType.Bool)]
23: internal static extern bool PostMessage(
24: int handle,
25: int message,
26: IntPtr wParam,
27: IntPtr lParam);
28: }
29: }
The class for the custom wrapper method is TestingMentor.TestTool.GlobalTester.SetDateFormat. There is a public enumeration for the short date and long date constants. One of these values must be assigned to the SetDateType property. The other property that must be set is the SetDateFormatPicture. The big change in the SetLocaleInfo() function is that the lcData type is a null-terminated string that MSDN refers to as a format picture. Current versions of Windows allow users to customize the order of the month, day and year, the format for each, and even allow different separators between the date elements. The format picture enables the user to select various format types in different orders for either the short date or the long date. See MSDN’s Month, Day, Year and Era Format Pictures for the various supported format types.
1: namespace TestingMentor.TestTool.GlobalTester
2: {
3: using System;
4:
5: public enum DateFormatType
6: {
7: ShortDate = 0x0000001F,
8: LongDate = 0x00000020
9: }
10:
11: public class CustomDateFormat
12: {
13: private string dateFormatPicture = string.Empty;
14: private int dateType = (int)DateFormatType.ShortDate;
15:
16: public string SetDateFormatPicture
17: {
18: set { this.dateFormatPicture = value; }
19: }
20:
21: public int SetDateType
22: {
23: set
24: {
25: if (value == (int)DateFormatType.ShortDate ||
26: value == (int)DateFormatType.LongDate)
27: {
28: this.dateType = value;
29: }
30: else
31: {
32: throw new ArgumentOutOfRangeException("Invalid DateType");
33: }
34: }
35: }
36:
37: public bool ChangeDateFormat()
38: {
39: bool success = false;
40: if (NativeMethods.SetLocaleInof(
41: NativeMethods.SystemDefaultLocale,
42: this.dateType,
43: this.dateFormatPicture))
44: {
45: NativeMethods.PostMessage(
46: NativeMethods.BroadcastMessage,
47: NativeMethods.SettingChangeMessage,
48: IntPtr.Zero,
49: IntPtr.Zero);
50: }
51:
52: return success;
53: }
54: }
55: }
Once the SetDateType and SetDateFormatPicture properties are assigned we simply have to call ChangeDateFormat() method to change the settings and broadcast the message to the system. The code snippet below illustrates how a tester would change the default long date format in an automated test to determine globalization support in the application under test. Customizing the date format is useful if the application under test uses a date string in any way. For example, if the application includes a function to insert a date string in an edit control, or if the date is printed as a header or footer in a document, or if a date string is appended to a record.
1: using TestingMentor.TestTool.GlobalTester;
2: ...
3: static void Main(string[] args)
4: {
5: CustomDateFormat date = new CustomDateFormat();
6: date.SetDateType = (int)DateType.LongDate;
7: date.SetDateFormatPicture = "[ dd % MM | yyyy ]";
8: if (date.ChangeDateFormat())
9: {
10: Console.WriteLine("Long date was changed");
11: }
12: }
Programmatically changing the date format is an easy way testers can customize date formats in their automated tests without having to manipulate the controls on Region and Language property sheet. Also note, that since the format picture is a string the order of the supported date format types is now controlled by the arrangement in the string, and the separator characters can be different between the day and month and the month and year as illustrated in the example above.
Modifying national conventions is one way to test for globalization support upstream and should be done early in the testing cycle rather than relying on a separate globalization testing cycle.
Next week I will discuss customizing the time format. Also, check out the beta release of the GlobalTester automation library that has this functionality and more and let me know what you think.

