Detecting Device Idiom on Xamarin UITest

When writing UI Tests with Xamarin UITest, you may want to take a different action when testing on a tablet form factor (idiom) or phone. There is some built in support in Xamarin UITest for detecting this on the iOS side, but nothing on the Android side. The solution I came up with was to use a backdoor method to give me the information I needed.

First, let’s take a look at how we create an instance if IApp, as we will need this for both iOS and Android. I am assuming the user has a basic understanding of how Xamarin UITest works.

When setting up your Test, you will using the FluentAPI built off of ConfigureApp to create an App for either Android or iOS. This creates a platform specific instance that implements IApp. 

public static IApp StartApp(Platform platform)
{
  if (platform == Platform.Android)
  {
    return ConfigureApp.Android.DoSomeCustomAndroidConfig.StartApp();
  } 
 
  return ConfigureApp.iOS..DoSomeCustomiOSConfig().StartApp();
}

In my base test class, I call StartApp and then check to see if I’m running on a phone or tablet.

[SetUp]
public void SetUp()
{
  App = StartApp(Platform);

 
  //Figure out what kind of Idiom we have.
  //Use the built in support for iOS and a backdoor method for Android
  var iOSApp = App as iOSApp;
  if (iOSApp != null)
  {
    IsPhone = iOSApp.Device.IsPhone;
  }
  else
  {
    var idiom = App.Invoke("BackdoorGetIdiom");
    IsPhone = idiom.Equals("Phone");
}

We first check to see if our App is of type iOSApp, and if it is, we’ll use the built in support in Xamarin UITest. If we are running on Android, then we’ll use the backdoor method approach, which I’ll outline next.

All I had to do was create my backdoor method and expose it from my MainActivity which is in my Xamarin Forms Android project.

[Export("BackdoorGetIdiom")]
public string BackdoorGetDeviceIdiom()
{
  return Device.Idiom.ToString();
}

You may need to add a reference to the Mono.Android.Export.dll. I found this in “C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\MonoAndroid\v6.0\Mono.Android.Export.dll”. Now, all you need to do is invoke the method as shown in my [SetUp] method.

There are various rules that you must follow when writing your backdoor methods which Xamarin outlines on their backdoor documentation page. The rules differ between iOS and Android so be sure to read them carefully.

Rolling X month / X week calculations in MDX using NonEmpty

Implementing a rolling x month or x week calculation in MDX is relatively straightforward if you have a calendar hierarchy in the multi-dimensional cube.  In some cases, you may only want to include dates where a measure is non-empty.  For example, if a user selects the current month, you may not want to include any future weeks that are empty in the rolling calculation.

Here’s some MDX that implements a rolling 6-week calculation that only includes non-empty dates.

Ancestor(Tail(NonEmpty(Descendants(StrToMember(@Calendar),[Date].[Calendar].[Date]),[Measures].[Registration Count])).Item(0),[Date].[Calendar].[Calendar Week of Year]).Lag(6):Ancestor(Tail(NonEmpty(Descendants(StrToMember(@Calendar),[Date].[Calendar].[Date]),[Measures].[Registration Count])).Item(0),[Date].[Calendar].[Calendar Week of Year])

This implementation uses the DESCENDANTS function to identify all child dates based on the current position in the Calendar hierarchy (represented by the parameter @Calendar).  We use the TAIL function to return the last date where the [Registration Count] measure is non-empty; then the ANCESTOR function returns the [Calendar Week of Year] associated to the date returned by the TAIL function. 

Once we identify the week based on the current position in the calendar hierarchy we can then use LAG(6) to navigate 6 weeks back.  The Range operator ‘:’ returns all the weeks from 6 weeks prior to the current week based on the calendar hierarchy position.

Here’s a complete MDX query using this logic.  Note: I’ve replaced StrToMember(@Calendar) with the member [Date].[Calendar].[Calendar Year].&[2015].&[Q2 2015].&[Apr 2015].

SELECT {[Measures].[Registration Count]} ON COLUMNS,  NON EMPTY [Date].[Calendar Week of Year].[Calendar Week of Year].MEMBERS ON ROWS
FROM [Cube]
WHERE Ancestor(Tail(NonEmpty(DESCENDANTS([Date].[Calendar].[Calendar Year].&[2015].&[Q2 2015].&[Apr 2015],[Date].[Calendar].[Date]),[Measures].[Registration Count])).ITEM(0),[Date].[Calendar].[Calendar Week of Year]).LAG(6):Ancestor(Tail(NonEmpty(DESCENDANTS([Date].[Calendar].[Calendar Year].&[2015].&[Q2 2015].&[Apr 2015],[Date].[Calendar].[Date]),[Measures].[Registration Count])).ITEM(0),[Date].[Calendar].[Calendar Week of Year])

Rolling X months / X weeks calculations in DAX

I recently implemented rolling 12 month and rolling 6 week measures in a SSAS tabular cube for product registration counts.

The rolling 12 month calculation is straight-forward and can be implemented using built-in DAX formulas.  This implementation is based off of Alberto Ferrari’s blog post. http://www.sqlbi.com/articles/rolling-12-months-average-in-dax/.

Registration Count Rolling 12 Mos:=CALCULATE([Registration Count Current Period], DATESBETWEEN(‘Date'[Date], SAMEPERIODLASTYEAR(NEXTDAY(LASTDATE(‘Date'[Date]))),LASTDATE(‘Date'[Date])))

The LASTDATE function returns the last date contained within the active filter context of the cube.  For example, if a user has filtered to July 2015 then last date will return July 31, 2015 based on a standard calendar implementation.  The function SAMEPERIODLASTYEAR is using the day after the last date in our filtered dataset and identifies the same date in the prior year.  DATESBETWEEN then identifies all the dates between last year’s and this year’s date.

The rolling 6 weeks implementation was less straightforward. I ended up implementing this calculation using a combination of a calculated column and measure in the cube.

First, I added a column called ‘ThisSaturday’ to the Date table in the cube.  For each date in the Date table, it calculates Saturday’s date in the same week.

The calculated column formula is listed below:

=CALCULATE(MIN(‘Date'[Date]), FILTER(ALL(‘Date’),’Date'[Day of Week]=7 && ‘Date'[Date] >= EARLIER(‘Date'[Date])))

We’re basically using the FILTER function to retrieve all the Saturday’s that are on or after the present date and then the MIN function identifies the Saturday that is nearest to the date.

Once we have this calculated column added to the date table, we can then apply it in our rolling 6 week measure.

Registration Count Rolling 6 Wks:=CALCULATE([Registration Count Current Period], DATESBETWEEN(‘Date'[Date], LASTDATE(‘Date'[ThisSaturday])-41,LASTDATE(‘Date'[ThisSaturday])))

In this measure, we use LASTDATE to identify the last date associated to the current filter context.  We then subtract 41 to calculate the date 6-weeks prior and use DATESBETWEEN to retrieve all the dates between this date and the Saturday of the last date in the filter context. 

SSAS Tabular Custom Prior Year Date Calculations

I am working on building a tabular cube and needed the ability to compare current fiscal year dates to prior fiscal year dates by matching the dates based on the same fiscal month and day of month and then performing calculations to compare sales between the current and prior fiscal year.  The built-in DAX date calculations didn’t appear to meet my needs (at least I wasn’t able to obtain the desired results using them), so I implemented the necessary behavior using an inactive relationship from my date dimension to the sales fact table.

As a first step, I added two additional columns to my tabular cube date dimensions that represents the prior year date key and date.  An example of the query is included below (note: for simplicity in this blog post I have not included all the date dimension attributes, an actual date dimension would likely have many more attributes than what I have included).  One challenge I encountered is an occasional mismatch between the number of days in a given fiscal month when comparing two fiscal years (some fiscal years have more days in a particular fiscal month than the prior year);  therefore, I am using a LEFT OUTER JOIN and then ISNULL to return an unknown for the LastYearDateKey column.  In addition, this column needs to have a unique value so the unknown is set to be –1 multiplied by the prior year date key.

SELECT DateKey, [Date], ISNULL(c.LastYearDateKey,-1*DateKey) [LastYearDateKey],c.LastYearDate [LastYearDate]
FROM DimDate d
LEFT OUTER JOIN (
    SELECT c.DateKey CurrentYearDateKey
          ,l.DateKey LastYearDateKey
          ,l.Date LastYearDate
    FROM DimDate l
    INNER JOIN (SELECT DateKey,Date,FiscalYear – 1 [FiscalYear],FiscalMonth,FiscalDayOfMonth FROM DimDate) c ON l.FiscalYear = c.FiscalYear
    AND l.FiscalMonth = c.FiscalMonth
    AND l.FiscalDayOfMonth = c.FiscalDayOfMonth) c ON d.DateKey = c.CurrentYearDateKey

Now that I have the necessary columns added to my dimension, I created an inactive relationship from my sales fact table to the new LastYearDateKey column in the date dimension (The primary / active relationship from the date table to the sales fact table is based on the current year date key).  The following shows the inactive relationship between the two tables (note that the active checkbox is unchecked).

image

With the inactive relationship established, I can create measures using DAX calculations that reference this inactive relationship.

Here’s an example of a measure in my sales table that uses the inactive relationship:

Sales Amount PY:=CALCULATE(SUM([SalesAmount]),USERELATIONSHIP(Dates[LastYearDateKey],Sales[GLDateKey])).

Now, I can compare sales in the prior year to the current year using the custom date matching (based on fiscal month and day of month) defined in my date dimension query.