1package com.xtremelabs.robolectric.matchers;
2
3import android.app.Service;
4import android.content.Context;
5import android.content.ContextWrapper;
6import android.content.Intent;
7import com.xtremelabs.robolectric.shadows.ShadowIntent;
8import org.hamcrest.Description;
9import org.junit.internal.matchers.TypeSafeMatcher;
10
11import java.util.Set;
12
13import static com.xtremelabs.robolectric.Robolectric.getShadowApplication;
14import static com.xtremelabs.robolectric.Robolectric.shadowOf;
15
16public class StartedServiceMatcher extends TypeSafeMatcher<Context> {
17    private final Intent expectedIntent;
18
19    private String message;
20
21    public StartedServiceMatcher(Intent expectedIntent) {
22        this.expectedIntent = expectedIntent;
23    }
24
25    public StartedServiceMatcher(String packageName, Class<? extends Service> expectedServiceClass) {
26        this(createIntent(packageName, expectedServiceClass));
27    }
28
29    public StartedServiceMatcher(Class<? extends Service> expectedServiceClass) {
30        this(createIntent(expectedServiceClass));
31    }
32
33    public StartedServiceMatcher(Class<? extends Service> expectedServiceClass, String expectedAction) {
34        this(createIntent(expectedServiceClass));
35
36        expectedIntent.setAction(expectedAction);
37    }
38
39    /**
40     * Check if the class of the intent and the keys of the intent's extras match
41     *
42     * @param actualContext
43     * @return
44     */
45    @Override
46    public boolean matchesSafely(Context actualContext) {
47        if (expectedIntent == null) {
48            message = "null intent (did you mean to expect null?)";
49            return false;
50        }
51
52        String expected = expectedIntent.toString();
53        message = "to start " + expected + ", but ";
54
55        Intent actualStartedIntent = shadowOf((ContextWrapper) actualContext).getNextStartedService();
56
57        if (actualStartedIntent == null) {
58            message += "didn't start anything";
59            return false;
60        }
61
62        ShadowIntent shadowIntent = shadowOf(actualStartedIntent);
63
64        //boolean intentsMatch = shadowOf(expectedIntent).realIntentEquals(shadowIntent);
65        // Test only that we are sending intent to the right service class
66        boolean intentsMatch = shadowOf(expectedIntent).getIntentClass().equals(shadowIntent.getIntentClass());
67        if (!intentsMatch) {
68            message += "started " + actualStartedIntent;
69        } else {
70            // Test that both intent extras have the same keys
71            Set<String> keys = shadowIntent.getExtras().keySet();
72            Set<String> expectedKeys = shadowOf(expectedIntent).getExtras().keySet();
73            intentsMatch = keys.equals(expectedKeys);
74            if(!intentsMatch){
75                message += "did not get the same extras keys";
76            }
77        }
78
79        return intentsMatch;
80    }
81
82    @Override
83    public void describeTo(Description description) {
84        description.appendText(message);
85    }
86
87    public static Intent createIntent(Class<? extends Service> serviceClass, String extraKey, String extraValue) {
88        Intent intent = createIntent(serviceClass);
89        intent.putExtra(extraKey, extraValue);
90        return intent;
91    }
92
93    public static Intent createIntent(Class<? extends Service> serviceClass, String action) {
94        Intent intent = createIntent(serviceClass);
95        intent.setAction(action);
96        return intent;
97    }
98
99    public static Intent createIntent(Class<? extends Service> serviceClass) {
100        Package pack = serviceClass.getPackage();
101        String packageName = "android.service";
102        // getPackage is returning null when run from tests
103        if(pack != null) {
104            pack.getName();
105        }
106        return createIntent(packageName, serviceClass);
107    }
108
109    public static Intent createIntent(String packageName, Class<? extends Service> serviceClass) {
110        Intent intent = new Intent();
111        intent.setClassName(packageName, serviceClass.getName());
112        intent.setClass(getShadowApplication().getApplicationContext(), serviceClass);
113        return intent;
114    }
115}
116