001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.hbtop.screen.top;
019
020import static org.apache.hadoop.hbase.hbtop.Record.entry;
021import static org.mockito.ArgumentMatchers.any;
022import static org.mockito.ArgumentMatchers.argThat;
023import static org.mockito.Mockito.inOrder;
024import static org.mockito.Mockito.verify;
025import static org.mockito.Mockito.when;
026
027import java.util.Arrays;
028import java.util.List;
029import java.util.stream.Collectors;
030import org.apache.hadoop.hbase.hbtop.Record;
031import org.apache.hadoop.hbase.hbtop.field.Field;
032import org.apache.hadoop.hbase.hbtop.field.FieldInfo;
033import org.apache.hadoop.hbase.hbtop.terminal.TerminalSize;
034import org.apache.hadoop.hbase.testclassification.SmallTests;
035import org.junit.jupiter.api.BeforeEach;
036import org.junit.jupiter.api.Tag;
037import org.junit.jupiter.api.Test;
038import org.junit.jupiter.api.extension.ExtendWith;
039import org.mockito.InOrder;
040import org.mockito.Mock;
041import org.mockito.junit.jupiter.MockitoExtension;
042
043@Tag(SmallTests.TAG)
044@ExtendWith(MockitoExtension.class)
045public class TestTopScreenPresenter {
046
047  private static final List<FieldInfo> TEST_FIELD_INFOS = Arrays.asList(
048    new FieldInfo(Field.REGION, 10, true), new FieldInfo(Field.REQUEST_COUNT_PER_SECOND, 10, true),
049    new FieldInfo(Field.LOCALITY, 10, true));
050
051  private static final List<Record> TEST_RECORDS = Arrays.asList(
052    Record.ofEntries(entry(Field.REGION, "region1"), entry(Field.REQUEST_COUNT_PER_SECOND, 1L),
053      entry(Field.LOCALITY, 0.3f)),
054    Record.ofEntries(entry(Field.REGION, "region2"), entry(Field.REQUEST_COUNT_PER_SECOND, 2L),
055      entry(Field.LOCALITY, 0.2f)),
056    Record.ofEntries(entry(Field.REGION, "region3"), entry(Field.REQUEST_COUNT_PER_SECOND, 3L),
057      entry(Field.LOCALITY, 0.1f)));
058
059  private static final Summary TEST_SUMMARY = new Summary("00:00:01", "3.0.0-SNAPSHOT",
060    "01234567-89ab-cdef-0123-456789abcdef", 3, 2, 1, 6, 1, 3.0, 300);
061
062  @Mock
063  private TopScreenView topScreenView;
064
065  @Mock
066  private TopScreenModel topScreenModel;
067
068  private TopScreenPresenter topScreenPresenter;
069
070  @BeforeEach
071  public void setup() {
072    when(topScreenView.getTerminalSize()).thenReturn(new TerminalSize(100, 100));
073    when(topScreenView.getPageSize()).thenReturn(100);
074
075    when(topScreenModel.getFieldInfos()).thenReturn(TEST_FIELD_INFOS);
076    when(topScreenModel.getFields())
077      .thenReturn(TEST_FIELD_INFOS.stream().map(FieldInfo::getField).collect(Collectors.toList()));
078    when(topScreenModel.getRecords()).thenReturn(TEST_RECORDS);
079    when(topScreenModel.getSummary()).thenReturn(TEST_SUMMARY);
080
081    topScreenPresenter =
082      new TopScreenPresenter(topScreenView, 3000, topScreenModel, null, Long.MAX_VALUE);
083  }
084
085  @Test
086  public void testRefresh() {
087    topScreenPresenter.init();
088    topScreenPresenter.refresh(true);
089
090    verify(topScreenView).showTopScreen(argThat(this::assertSummary), argThat(this::assertHeaders),
091      argThat(this::assertRecords),
092      argThat(selectedRecord -> assertSelectedRecord(selectedRecord, 0)));
093  }
094
095  @Test
096  public void testVerticalScrolling() {
097    topScreenPresenter.init();
098    topScreenPresenter.refresh(true);
099
100    topScreenPresenter.arrowDown();
101    topScreenPresenter.arrowDown();
102    topScreenPresenter.arrowDown();
103
104    topScreenPresenter.arrowDown();
105    topScreenPresenter.arrowDown();
106    topScreenPresenter.arrowDown();
107
108    topScreenPresenter.arrowUp();
109    topScreenPresenter.arrowUp();
110    topScreenPresenter.arrowUp();
111
112    topScreenPresenter.pageDown();
113    topScreenPresenter.pageDown();
114
115    topScreenPresenter.pageUp();
116    topScreenPresenter.pageUp();
117
118    InOrder inOrder = inOrder(topScreenView);
119    verifyVerticalScrolling(inOrder, 0);
120
121    verifyVerticalScrolling(inOrder, 1);
122    verifyVerticalScrolling(inOrder, 2);
123    verifyVerticalScrolling(inOrder, 2);
124
125    verifyVerticalScrolling(inOrder, 1);
126    verifyVerticalScrolling(inOrder, 0);
127    verifyVerticalScrolling(inOrder, 0);
128
129    verifyVerticalScrolling(inOrder, 2);
130    verifyVerticalScrolling(inOrder, 2);
131
132    verifyVerticalScrolling(inOrder, 0);
133    verifyVerticalScrolling(inOrder, 0);
134  }
135
136  private void verifyVerticalScrolling(InOrder inOrder, int expectedSelectedRecodeIndex) {
137    inOrder.verify(topScreenView).showTopScreen(any(), any(), any(),
138      argThat(selectedRecord -> assertSelectedRecord(selectedRecord, expectedSelectedRecodeIndex)));
139  }
140
141  @Test
142  public void testHorizontalScrolling() {
143    topScreenPresenter.init();
144    topScreenPresenter.refresh(true);
145
146    topScreenPresenter.arrowRight();
147    topScreenPresenter.arrowRight();
148    topScreenPresenter.arrowRight();
149
150    topScreenPresenter.arrowLeft();
151    topScreenPresenter.arrowLeft();
152    topScreenPresenter.arrowLeft();
153
154    topScreenPresenter.end();
155    topScreenPresenter.end();
156
157    topScreenPresenter.home();
158    topScreenPresenter.home();
159
160    InOrder inOrder = inOrder(topScreenView);
161    verifyHorizontalScrolling(inOrder, 3);
162
163    verifyHorizontalScrolling(inOrder, 2);
164    verifyHorizontalScrolling(inOrder, 1);
165    verifyHorizontalScrolling(inOrder, 1);
166
167    verifyHorizontalScrolling(inOrder, 2);
168    verifyHorizontalScrolling(inOrder, 3);
169    verifyHorizontalScrolling(inOrder, 3);
170
171    verifyHorizontalScrolling(inOrder, 1);
172    verifyHorizontalScrolling(inOrder, 1);
173
174    verifyHorizontalScrolling(inOrder, 3);
175    verifyHorizontalScrolling(inOrder, 3);
176  }
177
178  private void verifyHorizontalScrolling(InOrder inOrder, int expectedHeaderCount) {
179    inOrder.verify(topScreenView).showTopScreen(any(),
180      argThat(headers -> headers.size() == expectedHeaderCount), any(), any());
181  }
182
183  private boolean assertSummary(Summary actual) {
184    return actual.getCurrentTime().equals(TEST_SUMMARY.getCurrentTime())
185      && actual.getVersion().equals(TEST_SUMMARY.getVersion())
186      && actual.getClusterId().equals(TEST_SUMMARY.getClusterId())
187      && actual.getServers() == TEST_SUMMARY.getServers()
188      && actual.getLiveServers() == TEST_SUMMARY.getLiveServers()
189      && actual.getDeadServers() == TEST_SUMMARY.getDeadServers()
190      && actual.getRegionCount() == TEST_SUMMARY.getRegionCount()
191      && actual.getRitCount() == TEST_SUMMARY.getRitCount()
192      && actual.getAverageLoad() == TEST_SUMMARY.getAverageLoad()
193      && actual.getAggregateRequestPerSecond() == TEST_SUMMARY.getAggregateRequestPerSecond();
194  }
195
196  private boolean assertHeaders(List<Header> actual) {
197    List<Header> expected = TEST_FIELD_INFOS.stream()
198      .map(fi -> new Header(fi.getField(), fi.getDefaultLength())).collect(Collectors.toList());
199
200    if (actual.size() != expected.size()) {
201      return false;
202    }
203
204    for (int i = 0; i < actual.size(); i++) {
205      if (actual.get(i).getField() != expected.get(i).getField()) {
206        return false;
207      }
208      if (actual.get(i).getLength() != expected.get(i).getLength()) {
209        return false;
210      }
211    }
212
213    return true;
214  }
215
216  private boolean assertRecords(List<Record> actual) {
217    if (actual.size() != TEST_RECORDS.size()) {
218      return false;
219    }
220
221    for (int i = 0; i < actual.size(); i++) {
222      if (!assertRecord(actual.get(i), TEST_RECORDS.get(i))) {
223        return false;
224      }
225    }
226
227    return true;
228  }
229
230  private boolean assertSelectedRecord(Record actual, int expectedSelectedRecodeIndex) {
231    return assertRecord(actual, TEST_RECORDS.get(expectedSelectedRecodeIndex));
232  }
233
234  private boolean assertRecord(Record actual, Record expected) {
235    return actual.get(Field.REGION).equals(expected.get(Field.REGION))
236      && actual.get(Field.REQUEST_COUNT_PER_SECOND)
237        .equals(expected.get(Field.REQUEST_COUNT_PER_SECOND))
238      && actual.get(Field.LOCALITY).equals(expected.get(Field.LOCALITY));
239  }
240}