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 
17 package com.android.tv.recommendation;
18 
19 import static androidx.test.InstrumentationRegistry.getContext;
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertTrue;
22 
23 import com.android.tv.data.api.Channel;
24 import com.android.tv.recommendation.RecommendationUtils.ChannelRecordSortedMapHelper;
25 import com.android.tv.recommendation.Recommender.Evaluator;
26 import com.android.tv.testing.utils.Utils;
27 import java.util.ArrayList;
28 import java.util.List;
29 import org.junit.Before;
30 
31 /** Base test case for Recommendation Evaluator Unit tests. */
32 public abstract class EvaluatorTestCase<T extends Evaluator> {
33     private static final long INVALID_CHANNEL_ID = -1;
34 
35     private static final double SCORE_DELTA = 0.01;
36 
37     private ChannelRecordSortedMapHelper mChannelRecordSortedMap;
38     private RecommendationDataManager mDataManager;
39 
40     public T mEvaluator;
41 
42     @Before
setUp()43     public void setUp() {
44         mChannelRecordSortedMap = new ChannelRecordSortedMapHelper(getContext());
45         mDataManager =
46                 RecommendationUtils.createMockRecommendationDataManager(mChannelRecordSortedMap);
47         Recommender mRecommender = new FakeRecommender();
48         mEvaluator = createEvaluator();
49         mEvaluator.setRecommender(mRecommender);
50         mChannelRecordSortedMap.setRecommender(mRecommender);
51         mChannelRecordSortedMap.resetRandom(Utils.createTestRandom());
52     }
53 
54     /** Each evaluator test has to create Evaluator in {@code mEvaluator}. */
createEvaluator()55     public abstract T createEvaluator();
56 
addChannels(int numberOfChannels)57     public void addChannels(int numberOfChannels) {
58         mChannelRecordSortedMap.addChannels(numberOfChannels);
59     }
60 
addChannel()61     public Channel addChannel() {
62         return mChannelRecordSortedMap.addChannel();
63     }
64 
addRandomWatchLogs( long watchStartTimeMs, long watchEndTimeMs, long maxWatchDurationMs)65     public void addRandomWatchLogs(
66             long watchStartTimeMs, long watchEndTimeMs, long maxWatchDurationMs) {
67         assertTrue(
68                 mChannelRecordSortedMap.addRandomWatchLogs(
69                         watchStartTimeMs, watchEndTimeMs, maxWatchDurationMs));
70     }
71 
addWatchLog(long channelId, long watchStartTimeMs, long durationTimeMs)72     public void addWatchLog(long channelId, long watchStartTimeMs, long durationTimeMs) {
73         assertTrue(
74                 mChannelRecordSortedMap.addWatchLog(channelId, watchStartTimeMs, durationTimeMs));
75     }
76 
getChannelIdListSorted()77     public List<Long> getChannelIdListSorted() {
78         return new ArrayList<>(mChannelRecordSortedMap.keySet());
79     }
80 
getLatestWatchEndTimeMs()81     public long getLatestWatchEndTimeMs() {
82         long latestWatchEndTimeMs = 0;
83         for (ChannelRecord channelRecord : mChannelRecordSortedMap.values()) {
84             latestWatchEndTimeMs =
85                     Math.max(latestWatchEndTimeMs, channelRecord.getLastWatchEndTimeMs());
86         }
87         return latestWatchEndTimeMs;
88     }
89 
90     /** Check whether scores of each channels are valid. */
assertChannelScoresValid()91     protected void assertChannelScoresValid() {
92         assertEqualScores(
93                 Evaluator.NOT_RECOMMENDED, mEvaluator.evaluateChannel(INVALID_CHANNEL_ID));
94         assertEqualScores(
95                 Evaluator.NOT_RECOMMENDED,
96                 mEvaluator.evaluateChannel(mChannelRecordSortedMap.size()));
97 
98         for (long channelId : mChannelRecordSortedMap.keySet()) {
99             double score = mEvaluator.evaluateChannel(channelId);
100             assertTrue(
101                     "Channel " + channelId + " score of " + score + "is not valid",
102                     score == Evaluator.NOT_RECOMMENDED || (0.0 <= score && score <= 1.0));
103         }
104     }
105 
106     /** Notify that loading channels and watch logs are finished. */
notifyChannelAndWatchLogLoaded()107     protected void notifyChannelAndWatchLogLoaded() {
108         mEvaluator.onChannelRecordListChanged(new ArrayList<>(mChannelRecordSortedMap.values()));
109     }
110 
assertEqualScores(double expected, double actual)111     void assertEqualScores(double expected, double actual) {
112         assertEquals(expected, actual, SCORE_DELTA);
113     }
114 
assertEqualScores(String message, double expected, double actual)115     void assertEqualScores(String message, double expected, double actual) {
116         assertEquals(message, expected, actual, SCORE_DELTA);
117     }
118 
119     private class FakeRecommender extends Recommender {
FakeRecommender()120         public FakeRecommender() {
121             super(
122                     new Recommender.Listener() {
123                         @Override
124                         public void onRecommenderReady() {}
125 
126                         @Override
127                         public void onRecommendationChanged() {}
128                     },
129                     true,
130                     mDataManager);
131         }
132 
133         @Override
getChannelRecord(long channelId)134         public ChannelRecord getChannelRecord(long channelId) {
135             return mChannelRecordSortedMap.get(channelId);
136         }
137     }
138 }
139