1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.tv.dvr;
18
19import static org.mockito.Matchers.anyInt;
20import static org.mockito.Matchers.anyLong;
21import static org.mockito.Matchers.argThat;
22import static org.mockito.Matchers.eq;
23import static org.mockito.Matchers.longThat;
24import static org.mockito.Mockito.verify;
25import static org.mockito.Mockito.verifyNoMoreInteractions;
26import static org.mockito.Mockito.when;
27
28import android.media.tv.TvRecordingClient;
29import android.os.Build;
30import android.os.Handler;
31import android.os.Looper;
32import android.os.Message;
33import android.os.SystemClock;
34import android.support.test.filters.SdkSuppress;
35import android.test.AndroidTestCase;
36import android.test.suitebuilder.annotation.SmallTest;
37
38import com.android.tv.data.Channel;
39import com.android.tv.data.Program;
40import com.android.tv.dvr.RecordingTask.State;
41import com.android.tv.testing.FakeClock;
42import com.android.tv.testing.dvr.RecordingTestUtils;
43
44import org.hamcrest.BaseMatcher;
45import org.hamcrest.Description;
46import org.mockito.ArgumentMatcher;
47import org.mockito.Mock;
48import org.mockito.MockitoAnnotations;
49
50import java.util.concurrent.TimeUnit;
51
52/**
53 * Tests for {@link RecordingTask}.
54 */
55@SmallTest
56@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
57public class RecordingTaskTest extends AndroidTestCase {
58    private static final long DURATION = TimeUnit.MINUTES.toMillis(30);
59    private static final long START_OFFSET = Scheduler.MS_TO_WAKE_BEFORE_START;
60    public static final int CHANNEL_ID = 273;
61
62    private FakeClock mFakeClock;
63    private DvrDataManagerInMemoryImpl mDataManager;
64    @Mock Handler mMockHandler;
65    @Mock DvrManager mDvrManager;
66    @Mock DvrSessionManager mMockSessionManager;
67    @Mock TvRecordingClient mMockTvRecordingClient;
68
69    @Override
70    protected void setUp() throws Exception {
71        super.setUp();
72        if (Looper.myLooper() == null) {
73            Looper.prepare();
74        }
75        MockitoAnnotations.initMocks(this);
76        mFakeClock = FakeClock.createWithCurrentTime();
77        mDataManager = new DvrDataManagerInMemoryImpl(getContext(), mFakeClock);
78    }
79
80    public void testHandle_init() {
81        Channel channel = createTestChannel();
82        ScheduledRecording r = createRecording(channel);
83        RecordingTask task = createRecordingTask(r, channel);
84        String inputId = channel.getInputId();
85        when(mMockSessionManager.canAcquireDvrSession(inputId, channel)).thenReturn(true);
86        when(mMockSessionManager.createTvRecordingClient("tag", task, null))
87                .thenReturn(mMockTvRecordingClient);
88        when(mMockHandler.sendEmptyMessageDelayed(anyInt(), anyLong())).thenReturn(true);
89
90        long delay = START_OFFSET - RecordingTask.MS_BEFORE_START;
91        long uptime = SystemClock.uptimeMillis();
92        assertTrue(task.handleMessage(createMessage(RecordingTask.MESSAGE_INIT)));
93
94        assertEquals(State.CONNECTION_PENDING, task.getState());
95        verify(mMockSessionManager).canAcquireDvrSession(inputId, channel);
96        verify(mMockSessionManager).createTvRecordingClient("tag", task, null);
97        verify(mMockTvRecordingClient).tune(eq(inputId), eq(channel.getUri()));
98
99        verifySendMessageAt(RecordingTask.MESSAGE_START_RECORDING, uptime + delay);
100        verifyNoMoreInteractions(mMockHandler, mMockTvRecordingClient, mMockSessionManager);
101    }
102
103    private static Channel createTestChannel() {
104        return new Channel.Builder().setId(CHANNEL_ID).setDisplayName("Test Ch " + CHANNEL_ID)
105                .build();
106    }
107
108    public void testHandle_init_cannotAcquireSession() {
109        Channel channel = createTestChannel();
110        ScheduledRecording r = createRecording(channel);
111        r = mDataManager.addScheduledRecordingInternal(r);
112        RecordingTask task = createRecordingTask(r, channel);
113
114        when(mMockSessionManager.canAcquireDvrSession(channel.getInputId(), channel))
115                .thenReturn(false);
116
117        assertTrue(task.handleMessage(createMessage(RecordingTask.MESSAGE_INIT)));
118
119        assertEquals(State.ERROR, task.getState());
120        verifySendMessage(Scheduler.HandlerWrapper.MESSAGE_REMOVE);
121        ScheduledRecording updatedScheduledRecording = mDataManager
122                .getScheduledRecording(r.getId());
123        assertEquals("status", ScheduledRecording.STATE_RECORDING_FAILED,
124                updatedScheduledRecording.getState());
125    }
126
127    public void testOnConnected() {
128        Channel channel = createTestChannel();
129        ScheduledRecording r = createRecording(channel);
130        mDataManager.addScheduledRecording(r);
131        RecordingTask task = createRecordingTask(r, channel);
132
133        task.onTuned(channel.getUri());
134
135        assertEquals(State.CONNECTED, task.getState());
136    }
137
138    private ScheduledRecording createRecording(Channel c) {
139        long startTime = mFakeClock.currentTimeMillis() + START_OFFSET;
140        long endTime = startTime + DURATION;
141        return RecordingTestUtils.createTestRecordingWithPeriod(c.getId(), startTime, endTime);
142    }
143
144    private RecordingTask createRecordingTask(ScheduledRecording r, Channel channel) {
145        Program p = r.getProgramId() == ScheduledRecording.ID_NOT_SET ? null
146                : new Program.Builder().setId(r.getId()).build();
147        RecordingTask recordingTask = new RecordingTask(r, channel, mDvrManager,
148                mMockSessionManager, mDataManager, mFakeClock);
149        recordingTask.setHandler(mMockHandler);
150        return recordingTask;
151    }
152
153    private void verifySendMessage(int what) {
154        verify(mMockHandler).sendMessageAtTime(argThat(messageMatchesWhat(what)), anyLong());
155    }
156
157    private void verifySendMessageAt(int what, long when) {
158        verify(mMockHandler).sendMessageAtTime(argThat(messageMatchesWhat(what)), delta(when, 100));
159    }
160
161    private static long delta(final long value, final long delta) {
162        return longThat(new BaseMatcher<Long>() {
163            @Override
164            public boolean matches(Object item) {
165                Long other = (Long) item;
166                return other >= value - delta && other <= value + delta;
167            }
168
169            @Override
170            public void describeTo(Description description) {
171                description.appendText("eq " + value + "±" + delta);
172
173            }
174        });
175    }
176
177    private Message createMessage(int what) {
178        Message msg = new Message();
179        msg.setTarget(mMockHandler);
180        msg.what = what;
181        return msg;
182    }
183
184    public static ArgumentMatcher<Message> messageMatchesWhat(final int what) {
185        return new ArgumentMatcher<Message>() {
186            @Override
187            public boolean matches(Object argument) {
188                Message message = (Message) argument;
189                return message.what == what;
190            }
191        };
192    }
193}