fix: 引入Settings的Module
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.car.qc;
|
||||
|
||||
import static com.android.car.qc.QCItem.QC_TYPE_ACTION_SWITCH;
|
||||
import static com.android.car.qc.QCItem.QC_TYPE_ACTION_TOGGLE;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.testng.Assert.assertThrows;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.graphics.drawable.Icon;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class QCActionItemTest extends QCItemTestCase<QCActionItem> {
|
||||
private static final String TEST_CONTENT_DESCRIPTION = "test_content_description";
|
||||
|
||||
@Test
|
||||
public void onCreate_invalidType_throwsException() {
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> createAction("INVALID_TYPE", /* action= */ null,
|
||||
/* disabledAction= */ null, /* icon= */ null, /* contentDescription=*/
|
||||
null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onCreateSwitch_hasCorrectType() {
|
||||
QCActionItem action = createAction(QC_TYPE_ACTION_SWITCH, /* action= */ null,
|
||||
/* disabledAction= */ null, /* icon= */null, /* contentDescription=*/ null);
|
||||
assertThat(action.getType()).isEqualTo(QC_TYPE_ACTION_SWITCH);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onCreateToggle_hasCorrectType() {
|
||||
QCActionItem action = createAction(QC_TYPE_ACTION_TOGGLE, /* action= */ null,
|
||||
/* disabledAction= */ null, /* icon= */ null, /* contentDescription=*/ null);
|
||||
assertThat(action.getType()).isEqualTo(QC_TYPE_ACTION_TOGGLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBundle_nullActions_noCrash() {
|
||||
QCActionItem action = createAction(QC_TYPE_ACTION_TOGGLE, /* action= */ null,
|
||||
/* disabledAction= */ null, mDefaultIcon, /* contentDescription=*/ null);
|
||||
writeAndLoadFromBundle(action);
|
||||
// Test passes if this doesn't crash
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBundle_nullIcon_noCrash() {
|
||||
QCActionItem action = createAction(QC_TYPE_ACTION_TOGGLE, mDefaultAction,
|
||||
mDefaultDisabledAction, /* icon= */ null, /* contentDescription=*/ null);
|
||||
writeAndLoadFromBundle(action);
|
||||
// Test passes if this doesn't crash
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBundle_switch_accurateData() {
|
||||
QCActionItem action = createAction(QC_TYPE_ACTION_SWITCH, mDefaultAction,
|
||||
mDefaultDisabledAction, /* icon= */ null, TEST_CONTENT_DESCRIPTION);
|
||||
QCActionItem newAction = writeAndLoadFromBundle(action);
|
||||
assertThat(newAction.getType()).isEqualTo(QC_TYPE_ACTION_SWITCH);
|
||||
assertThat(newAction.isChecked()).isTrue();
|
||||
assertThat(newAction.isEnabled()).isTrue();
|
||||
assertThat(newAction.isClickableWhileDisabled()).isFalse();
|
||||
assertThat(newAction.getPrimaryAction()).isNotNull();
|
||||
assertThat(newAction.getIcon()).isNull();
|
||||
assertThat(newAction.getContentDescription()).isEqualTo(TEST_CONTENT_DESCRIPTION);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBundle_toggle_accurateDate() {
|
||||
QCActionItem action = createAction(QC_TYPE_ACTION_TOGGLE, mDefaultAction,
|
||||
mDefaultDisabledAction, mDefaultIcon, TEST_CONTENT_DESCRIPTION);
|
||||
QCActionItem newAction = writeAndLoadFromBundle(action);
|
||||
assertThat(newAction.getType()).isEqualTo(QC_TYPE_ACTION_TOGGLE);
|
||||
assertThat(newAction.isChecked()).isTrue();
|
||||
assertThat(newAction.isEnabled()).isTrue();
|
||||
assertThat(newAction.isClickableWhileDisabled()).isFalse();
|
||||
assertThat(newAction.getPrimaryAction()).isNotNull();
|
||||
assertThat(newAction.getIcon()).isNotNull();
|
||||
assertThat(newAction.getContentDescription()).isEqualTo(TEST_CONTENT_DESCRIPTION);
|
||||
}
|
||||
|
||||
private QCActionItem createAction(String type, PendingIntent action,
|
||||
PendingIntent disabledAction, Icon icon, String contentDescription) {
|
||||
return new QCActionItem.Builder(type)
|
||||
.setChecked(true)
|
||||
.setEnabled(true)
|
||||
.setAction(action)
|
||||
.setDisabledClickAction(disabledAction)
|
||||
.setIcon(icon)
|
||||
.setContentDescription(contentDescription)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.car.qc;
|
||||
|
||||
import android.R;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
public abstract class QCItemTestCase<T extends QCItem> {
|
||||
protected static final String BUNDLE_KEY = "BUNDLE_KEY";
|
||||
protected static final String TEST_TITLE = "TEST TITLE";
|
||||
protected static final String TEST_SUBTITLE = "TEST SUBTITLE";
|
||||
|
||||
protected final Context mContext = ApplicationProvider.getApplicationContext();
|
||||
|
||||
protected PendingIntent mDefaultAction = PendingIntent.getActivity(mContext,
|
||||
/* requestCode= */ 0, new Intent(), PendingIntent.FLAG_IMMUTABLE);
|
||||
protected PendingIntent mDefaultDisabledAction = PendingIntent.getActivity(mContext,
|
||||
/* requestCode= */ 1, new Intent(), PendingIntent.FLAG_IMMUTABLE);
|
||||
protected Icon mDefaultIcon = Icon.createWithResource(mContext, R.drawable.btn_star);
|
||||
|
||||
protected T writeAndLoadFromBundle(T item) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(BUNDLE_KEY, item);
|
||||
return bundle.getParcelable(BUNDLE_KEY);
|
||||
}
|
||||
}
|
||||
62
car-qc-lib/tests/unit/src/com/android/car/qc/QCListTest.java
Normal file
62
car-qc-lib/tests/unit/src/com/android/car/qc/QCListTest.java
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.car.qc;
|
||||
|
||||
import static com.android.car.qc.QCItem.QC_TYPE_LIST;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class QCListTest extends QCItemTestCase<QCList> {
|
||||
|
||||
@Test
|
||||
public void onCreate_hasCorrectType() {
|
||||
QCList list = createList(Collections.emptyList());
|
||||
assertThat(list.getType()).isEqualTo(QC_TYPE_LIST);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createFromParcel_accurateData() {
|
||||
QCRow row = new QCRow.Builder()
|
||||
.setTitle(TEST_TITLE)
|
||||
.setSubtitle(TEST_SUBTITLE)
|
||||
.setIcon(mDefaultIcon)
|
||||
.setPrimaryAction(mDefaultAction)
|
||||
.build();
|
||||
|
||||
QCList list = createList(Collections.singletonList(row));
|
||||
QCList newList = writeAndLoadFromBundle(list);
|
||||
assertThat(newList.getType()).isEqualTo(QC_TYPE_LIST);
|
||||
assertThat(newList.getRows().size()).isEqualTo(1);
|
||||
}
|
||||
|
||||
private QCList createList(List<QCRow> rows) {
|
||||
QCList.Builder builder = new QCList.Builder();
|
||||
for (QCRow row : rows) {
|
||||
builder.addRow(row);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
124
car-qc-lib/tests/unit/src/com/android/car/qc/QCRowTest.java
Normal file
124
car-qc-lib/tests/unit/src/com/android/car/qc/QCRowTest.java
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.car.qc;
|
||||
|
||||
import static com.android.car.qc.QCItem.QC_TYPE_ACTION_SWITCH;
|
||||
import static com.android.car.qc.QCItem.QC_TYPE_ROW;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.graphics.drawable.Icon;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class QCRowTest extends QCItemTestCase<QCRow> {
|
||||
|
||||
@Test
|
||||
public void onCreate_hasCorrectType() {
|
||||
QCRow row = createRow(/* action= */ null, /* disabledAction= */ null, /* icon= */ null);
|
||||
assertThat(row.getType()).isEqualTo(QC_TYPE_ROW);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBundle_nullActions_noCrash() {
|
||||
QCRow row = createRow(/* action= */ null, /* disabledAction= */ null, mDefaultIcon);
|
||||
writeAndLoadFromBundle(row);
|
||||
// Test passes if this doesn't crash
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBundle_nullIcon_noCrash() {
|
||||
QCRow row = createRow(mDefaultAction, mDefaultDisabledAction, /* icon= */ null);
|
||||
writeAndLoadFromBundle(row);
|
||||
// Test passes if this doesn't crash
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createFromParcel_accurateData() {
|
||||
QCRow row = createRow(mDefaultAction, mDefaultDisabledAction, mDefaultIcon);
|
||||
QCRow newRow = writeAndLoadFromBundle(row);
|
||||
assertThat(newRow.getType()).isEqualTo(QC_TYPE_ROW);
|
||||
assertThat(newRow.getTitle()).isEqualTo(TEST_TITLE);
|
||||
assertThat(newRow.getSubtitle()).isEqualTo(TEST_SUBTITLE);
|
||||
assertThat(newRow.getPrimaryAction()).isNotNull();
|
||||
assertThat(newRow.getStartIcon()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createFromParcel_accurateData_startItem() {
|
||||
QCActionItem item = new QCActionItem.Builder(QC_TYPE_ACTION_SWITCH).build();
|
||||
|
||||
QCRow row = createRow(/* action= */ null, /* disabledAction= */ null, /* icon= */ null,
|
||||
Collections.singletonList(item), Collections.emptyList(), Collections.emptyList());
|
||||
QCRow newRow = writeAndLoadFromBundle(row);
|
||||
assertThat(newRow.getStartItems().size()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createFromParcel_accurateData_endItem() {
|
||||
QCActionItem item = new QCActionItem.Builder(QC_TYPE_ACTION_SWITCH).build();
|
||||
|
||||
QCRow row = createRow(/* action= */ null, /* disabledAction= */ null, /* icon= */ null,
|
||||
Collections.emptyList(), Collections.singletonList(item), Collections.emptyList());
|
||||
QCRow newRow = writeAndLoadFromBundle(row);
|
||||
assertThat(newRow.getEndItems().size()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createFromParcel_accurateData_slider() {
|
||||
QCSlider slider = new QCSlider.Builder().build();
|
||||
|
||||
QCRow row = createRow(/* action= */ null, /* disabledAction= */ null, /* icon= */ null,
|
||||
Collections.emptyList(), Collections.emptyList(),
|
||||
Collections.singletonList(slider));
|
||||
QCRow newRow = writeAndLoadFromBundle(row);
|
||||
assertThat(newRow.getSlider()).isNotNull();
|
||||
}
|
||||
|
||||
private QCRow createRow(PendingIntent action, PendingIntent disabledAction, Icon icon) {
|
||||
return createRow(action, disabledAction, icon, Collections.emptyList(),
|
||||
Collections.emptyList(), Collections.emptyList());
|
||||
}
|
||||
|
||||
private QCRow createRow(PendingIntent action, PendingIntent disabledAction, Icon icon,
|
||||
List<QCActionItem> startItems, List<QCActionItem> endItems, List<QCSlider> sliders) {
|
||||
QCRow.Builder builder = new QCRow.Builder()
|
||||
.setTitle(TEST_TITLE)
|
||||
.setSubtitle(TEST_SUBTITLE)
|
||||
.setIcon(icon)
|
||||
.setPrimaryAction(action)
|
||||
.setDisabledClickAction(disabledAction);
|
||||
for (QCActionItem item : startItems) {
|
||||
builder.addStartItem(item);
|
||||
}
|
||||
for (QCActionItem item : endItems) {
|
||||
builder.addEndItem(item);
|
||||
}
|
||||
for (QCSlider slider : sliders) {
|
||||
builder.addSlider(slider);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.car.qc;
|
||||
|
||||
import static com.android.car.qc.QCItem.QC_TYPE_SLIDER;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class QCSliderTest extends QCItemTestCase<QCSlider> {
|
||||
private static final int MIN = 50;
|
||||
private static final int MAX = 150;
|
||||
private static final int VALUE = 75;
|
||||
|
||||
@Test
|
||||
public void onCreate_hasCorrectType() {
|
||||
QCSlider slider = createSlider(/* action= */ null, /* disabledAction= */ null);
|
||||
assertThat(slider.getType()).isEqualTo(QC_TYPE_SLIDER);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBundle_nullActions_noCrash() {
|
||||
QCSlider slider = createSlider(/* action= */ null, /* disabledAction= */ null);
|
||||
writeAndLoadFromBundle(slider);
|
||||
// Test passes if this doesn't crash
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createFromParcel_accurateData() {
|
||||
QCSlider slider = createSlider(mDefaultAction, mDefaultDisabledAction);
|
||||
QCSlider newSlider = writeAndLoadFromBundle(slider);
|
||||
assertThat(newSlider.getType()).isEqualTo(QC_TYPE_SLIDER);
|
||||
assertThat(newSlider.getPrimaryAction()).isNotNull();
|
||||
assertThat(newSlider.getDisabledClickAction()).isNotNull();
|
||||
assertThat(newSlider.getMin()).isEqualTo(MIN);
|
||||
assertThat(newSlider.getMax()).isEqualTo(MAX);
|
||||
assertThat(newSlider.getValue()).isEqualTo(VALUE);
|
||||
}
|
||||
|
||||
private QCSlider createSlider(PendingIntent action, PendingIntent disabledAction) {
|
||||
return new QCSlider.Builder()
|
||||
.setMin(MIN)
|
||||
.setMax(MAX)
|
||||
.setValue(VALUE)
|
||||
.setInputAction(action)
|
||||
.setDisabledClickAction(disabledAction)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
77
car-qc-lib/tests/unit/src/com/android/car/qc/QCTileTest.java
Normal file
77
car-qc-lib/tests/unit/src/com/android/car/qc/QCTileTest.java
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.car.qc;
|
||||
|
||||
import static com.android.car.qc.QCItem.QC_TYPE_TILE;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.graphics.drawable.Icon;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class QCTileTest extends QCItemTestCase<QCTile> {
|
||||
|
||||
@Test
|
||||
public void onCreate_hasCorrectType() {
|
||||
QCTile tile = createTile(/* action= */ null, /* disabledAction= */ null, /* icon= */ null);
|
||||
assertThat(tile.getType()).isEqualTo(QC_TYPE_TILE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBundle_nullAction_noCrash() {
|
||||
QCTile tile = createTile(/* action= */ null, /* disabledAction= */ null, mDefaultIcon);
|
||||
writeAndLoadFromBundle(tile);
|
||||
// Test passes if this doesn't crash
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBundle_nullIcon_noCrash() {
|
||||
QCTile tile = createTile(mDefaultAction, mDefaultDisabledAction, /* icon= */ null);
|
||||
writeAndLoadFromBundle(tile);
|
||||
// Test passes if this doesn't crash
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createFromParcel_accurateData() {
|
||||
QCTile tile = createTile(mDefaultAction, mDefaultDisabledAction, mDefaultIcon);
|
||||
QCTile newTile = writeAndLoadFromBundle(tile);
|
||||
assertThat(newTile.getType()).isEqualTo(QC_TYPE_TILE);
|
||||
assertThat(newTile.getSubtitle()).isEqualTo(TEST_SUBTITLE);
|
||||
assertThat(newTile.isChecked()).isTrue();
|
||||
assertThat(newTile.isEnabled()).isTrue();
|
||||
assertThat(newTile.getPrimaryAction()).isNotNull();
|
||||
assertThat(newTile.getDisabledClickAction()).isNotNull();
|
||||
assertThat(newTile.getIcon()).isNotNull();
|
||||
}
|
||||
|
||||
private QCTile createTile(PendingIntent action, PendingIntent disabledAction, Icon icon) {
|
||||
return new QCTile.Builder()
|
||||
.setSubtitle(TEST_SUBTITLE)
|
||||
.setChecked(true)
|
||||
.setEnabled(true)
|
||||
.setAction(action)
|
||||
.setDisabledClickAction(disabledAction)
|
||||
.setIcon(icon)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.car.qc.controller;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.android.car.qc.QCItem;
|
||||
import com.android.car.qc.QCTile;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public abstract class BaseQCControllerTestCase<T extends BaseQCController> {
|
||||
|
||||
protected final Context mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
|
||||
protected abstract T getController();
|
||||
|
||||
@Test
|
||||
public void listen_updateListeningCalled() {
|
||||
T spiedController = spy(getController());
|
||||
spiedController.listen(true);
|
||||
verify(spiedController).updateListening();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addObserver_updateListeningCalled() {
|
||||
Observer<QCItem> observer = mock(Observer.class);
|
||||
T spiedController = spy(getController());
|
||||
spiedController.addObserver(observer);
|
||||
verify(spiedController).updateListening();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeObserver_updateListeningCalled() {
|
||||
Observer<QCItem> observer = mock(Observer.class);
|
||||
T spiedController = spy(getController());
|
||||
spiedController.removeObserver(observer);
|
||||
verify(spiedController).updateListening();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onQCItemUpdated_observersNotified() {
|
||||
Observer<QCItem> observer = mock(Observer.class);
|
||||
getController().addObserver(observer);
|
||||
getController().onQCItemUpdated(new QCTile.Builder().build());
|
||||
verify(observer).onChanged(any(QCItem.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDestroy_cleanUpController() {
|
||||
Observer<QCItem> observer = mock(Observer.class);
|
||||
getController().addObserver(observer);
|
||||
getController().listen(true);
|
||||
getController().destroy();
|
||||
assertThat(getController().mObservers.size()).isEqualTo(0);
|
||||
assertThat(getController().mShouldListen).isFalse();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.car.qc.controller;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.car.qc.QCItem;
|
||||
import com.android.car.qc.provider.BaseLocalQCProvider;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class LocalQCControllerTest extends BaseQCControllerTestCase<LocalQCController> {
|
||||
|
||||
private LocalQCController mController;
|
||||
private BaseLocalQCProvider mProvider;
|
||||
|
||||
@Override
|
||||
protected LocalQCController getController() {
|
||||
if (mController == null) {
|
||||
mProvider = mock(BaseLocalQCProvider.class);
|
||||
mController = new LocalQCController(mContext, mProvider);
|
||||
}
|
||||
return mController;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onCreate_setsProviderNotifier() {
|
||||
getController(); // instantiate
|
||||
verify(mProvider).setNotifier(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBind_updatesQCItem() {
|
||||
Observer<QCItem> observer = mock(Observer.class);
|
||||
LocalQCController spiedController = spy(getController());
|
||||
spiedController.addObserver(observer);
|
||||
Mockito.reset(mProvider);
|
||||
spiedController.bind();
|
||||
verify(mProvider).getQCItem();
|
||||
verify(spiedController).onQCItemUpdated(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateListening_updatesProviderListening() {
|
||||
Observer<QCItem> observer = mock(Observer.class);
|
||||
getController().addObserver(observer);
|
||||
getController().listen(true);
|
||||
verify(mProvider).shouldListen(true);
|
||||
getController().listen(false);
|
||||
verify(mProvider).shouldListen(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateListening_listen_updatesQCItem() {
|
||||
Observer<QCItem> observer = mock(Observer.class);
|
||||
LocalQCController spiedController = spy(getController());
|
||||
spiedController.addObserver(observer);
|
||||
Mockito.reset(mProvider);
|
||||
spiedController.listen(true);
|
||||
verify(mProvider).getQCItem();
|
||||
verify(spiedController).onQCItemUpdated(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDestroy_callsProviderDestroy() {
|
||||
getController().destroy();
|
||||
verify(mProvider).onDestroy();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.car.qc.controller;
|
||||
|
||||
import static com.android.car.qc.provider.BaseQCProvider.EXTRA_URI;
|
||||
import static com.android.car.qc.testutils.TestQCProvider.IS_DESTROYED_KEY;
|
||||
import static com.android.car.qc.testutils.TestQCProvider.IS_SUBSCRIBED_KEY;
|
||||
import static com.android.car.qc.testutils.TestQCProvider.KEY_DEFAULT;
|
||||
import static com.android.car.qc.testutils.TestQCProvider.METHOD_IS_DESTROYED;
|
||||
import static com.android.car.qc.testutils.TestQCProvider.METHOD_IS_SUBSCRIBED;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.notNull;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.ContentProviderClient;
|
||||
import android.content.ContentResolver;
|
||||
import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import com.android.car.qc.QCItem;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class RemoteQCControllerTest extends BaseQCControllerTestCase<RemoteQCController> {
|
||||
|
||||
private final Uri mDefaultUri = Uri.parse(
|
||||
"content://com.android.car.qc.testutils.AllowedTestQCProvider/" + KEY_DEFAULT);
|
||||
|
||||
private RemoteQCController mController;
|
||||
|
||||
@Override
|
||||
protected RemoteQCController getController() {
|
||||
if (mController == null) {
|
||||
mController = new RemoteQCController(mContext, mDefaultUri, mContext.getMainExecutor());
|
||||
}
|
||||
return mController;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBind_updatesQCItem() {
|
||||
Observer<QCItem> observer = mock(Observer.class);
|
||||
RemoteQCController spiedController = spy(getController());
|
||||
spiedController.addObserver(observer);
|
||||
spiedController.bind();
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
|
||||
verify(spiedController).onQCItemUpdated(notNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateListening_listen_updatesQCItem() {
|
||||
Observer<QCItem> observer = mock(Observer.class);
|
||||
RemoteQCController spiedController = spy(getController());
|
||||
spiedController.addObserver(observer);
|
||||
spiedController.listen(true);
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
|
||||
verify(spiedController).onQCItemUpdated(notNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateListening_listen_providerSubscribed() throws RemoteException {
|
||||
Observer<QCItem> observer = mock(Observer.class);
|
||||
getController().addObserver(observer);
|
||||
getController().listen(true);
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
|
||||
|
||||
Bundle extras = new Bundle();
|
||||
extras.putParcelable(EXTRA_URI, mDefaultUri);
|
||||
Bundle res = getController().getClient().call(METHOD_IS_SUBSCRIBED, null, extras);
|
||||
assertThat(res).isNotNull();
|
||||
boolean isSubscribed = res.getBoolean(IS_SUBSCRIBED_KEY, false);
|
||||
assertThat(isSubscribed).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateListening_doNotListen_providerUnsubscribed() throws RemoteException {
|
||||
Observer<QCItem> observer = mock(Observer.class);
|
||||
getController().addObserver(observer);
|
||||
getController().listen(true);
|
||||
getController().listen(false);
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
|
||||
|
||||
Bundle extras = new Bundle();
|
||||
extras.putParcelable(EXTRA_URI, mDefaultUri);
|
||||
Bundle res = getController().getClient().call(METHOD_IS_SUBSCRIBED, null, extras);
|
||||
assertThat(res).isNotNull();
|
||||
boolean isSubscribed = res.getBoolean(IS_SUBSCRIBED_KEY, true);
|
||||
assertThat(isSubscribed).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateListening_listen_registerContentObserver() {
|
||||
ContentResolver resolver = mock(ContentResolver.class);
|
||||
when(mContext.getContentResolver()).thenReturn(resolver);
|
||||
when(resolver.acquireContentProviderClient(mDefaultUri)).thenReturn(
|
||||
mock(ContentProviderClient.class));
|
||||
Observer<QCItem> observer = mock(Observer.class);
|
||||
getController().addObserver(observer);
|
||||
getController().listen(true);
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
|
||||
verify(resolver).registerContentObserver(eq(mDefaultUri), eq(true),
|
||||
any(ContentObserver.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateListening_doNotListen_unregisterContentObserver() {
|
||||
ContentResolver resolver = mock(ContentResolver.class);
|
||||
when(mContext.getContentResolver()).thenReturn(resolver);
|
||||
when(resolver.acquireContentProviderClient(mDefaultUri)).thenReturn(
|
||||
mock(ContentProviderClient.class));
|
||||
Observer<QCItem> observer = mock(Observer.class);
|
||||
getController().addObserver(observer);
|
||||
getController().listen(true);
|
||||
getController().listen(false);
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
|
||||
verify(resolver).unregisterContentObserver(any(ContentObserver.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDestroy_callsProviderOnDestroy() throws RemoteException {
|
||||
Observer<QCItem> observer = mock(Observer.class);
|
||||
getController().addObserver(observer);
|
||||
getController().listen(true);
|
||||
getController().listen(false);
|
||||
getController().destroy();
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
|
||||
|
||||
Bundle extras = new Bundle();
|
||||
extras.putParcelable(EXTRA_URI, mDefaultUri);
|
||||
Bundle res = getController().getClient().call(METHOD_IS_DESTROYED, null, extras);
|
||||
assertThat(res).isNotNull();
|
||||
boolean isDestroyed = res.getBoolean(IS_DESTROYED_KEY, false);
|
||||
assertThat(isDestroyed).isTrue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.car.qc.provider;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.car.qc.QCItem;
|
||||
import com.android.car.qc.QCTile;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class BaseLocalQCProviderTest {
|
||||
|
||||
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||
private TestBaseLocalQCProvider mProvider;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mProvider = new TestBaseLocalQCProvider(mContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getQCItem_returnsItem() {
|
||||
QCItem item = mProvider.getQCItem();
|
||||
assertThat(item).isNotNull();
|
||||
assertThat(item instanceof QCTile).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listen_callsOnSubscribed() {
|
||||
mProvider.shouldListen(true);
|
||||
assertThat(mProvider.isSubscribed()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stopListening_callsOnUnsubscribed() {
|
||||
mProvider.shouldListen(true);
|
||||
mProvider.shouldListen(false);
|
||||
assertThat(mProvider.isSubscribed()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notifyChange_updateNotified() {
|
||||
BaseLocalQCProvider.Notifier notifier = mock(BaseLocalQCProvider.Notifier.class);
|
||||
mProvider.setNotifier(notifier);
|
||||
mProvider.notifyChange();
|
||||
verify(notifier).notifyUpdate();
|
||||
}
|
||||
|
||||
private static class TestBaseLocalQCProvider extends BaseLocalQCProvider {
|
||||
|
||||
private boolean mIsSubscribed;
|
||||
|
||||
TestBaseLocalQCProvider(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QCItem getQCItem() {
|
||||
return new QCTile.Builder().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSubscribed() {
|
||||
mIsSubscribed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onUnsubscribed() {
|
||||
mIsSubscribed = false;
|
||||
}
|
||||
|
||||
boolean isSubscribed() {
|
||||
return mIsSubscribed;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.car.qc.provider;
|
||||
|
||||
import static com.android.car.qc.provider.BaseQCProvider.EXTRA_ITEM;
|
||||
import static com.android.car.qc.provider.BaseQCProvider.EXTRA_URI;
|
||||
import static com.android.car.qc.provider.BaseQCProvider.METHOD_BIND;
|
||||
import static com.android.car.qc.provider.BaseQCProvider.METHOD_DESTROY;
|
||||
import static com.android.car.qc.provider.BaseQCProvider.METHOD_SUBSCRIBE;
|
||||
import static com.android.car.qc.provider.BaseQCProvider.METHOD_UNSUBSCRIBE;
|
||||
import static com.android.car.qc.testutils.TestQCProvider.IS_DESTROYED_KEY;
|
||||
import static com.android.car.qc.testutils.TestQCProvider.IS_SUBSCRIBED_KEY;
|
||||
import static com.android.car.qc.testutils.TestQCProvider.KEY_DEFAULT;
|
||||
import static com.android.car.qc.testutils.TestQCProvider.KEY_SLOW;
|
||||
import static com.android.car.qc.testutils.TestQCProvider.METHOD_IS_DESTROYED;
|
||||
import static com.android.car.qc.testutils.TestQCProvider.METHOD_IS_SUBSCRIBED;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.testng.Assert.assertThrows;
|
||||
|
||||
import android.content.ContentProviderClient;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.car.qc.QCItem;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class BaseQCProviderTest {
|
||||
|
||||
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||
private final Uri mDefaultUri = Uri.parse(
|
||||
"content://com.android.car.qc.testutils.AllowedTestQCProvider/" + KEY_DEFAULT);
|
||||
private final Uri mSlowUri =
|
||||
Uri.parse("content://com.android.car.qc.testutils.AllowedTestQCProvider/" + KEY_SLOW);
|
||||
private final Uri mDeniedUri =
|
||||
Uri.parse("content://com.android.car.qc.testutils.DeniedTestQCProvider");
|
||||
|
||||
@Test
|
||||
public void callOnBind_allowed_returnsItem() throws RemoteException {
|
||||
ContentProviderClient provider = getClient(mDefaultUri);
|
||||
assertThat(provider).isNotNull();
|
||||
Bundle extras = new Bundle();
|
||||
extras.putParcelable(EXTRA_URI, mDefaultUri);
|
||||
Bundle res = provider.call(METHOD_BIND, null, extras);
|
||||
assertThat(res).isNotNull();
|
||||
res.setClassLoader(QCItem.class.getClassLoader());
|
||||
Parcelable parcelable = res.getParcelable(EXTRA_ITEM);
|
||||
assertThat(parcelable).isNotNull();
|
||||
assertThat(parcelable instanceof QCItem).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void callOnBind_noUri_throwsIllegalArgumentException() throws RemoteException {
|
||||
ContentProviderClient provider = getClient(mDefaultUri);
|
||||
assertThat(provider).isNotNull();
|
||||
Bundle extras = new Bundle();
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> provider.call(METHOD_BIND, null, extras));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void callOnBind_slowOperation_throwsRuntimeException() {
|
||||
ContentProviderClient provider = getClient(mSlowUri);
|
||||
assertThat(provider).isNotNull();
|
||||
Bundle extras = new Bundle();
|
||||
extras.putParcelable(EXTRA_URI, mSlowUri);
|
||||
assertThrows(RuntimeException.class,
|
||||
() -> provider.call(METHOD_BIND, null, extras));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void callOnBind_notAllowed_throwsSecurityException() {
|
||||
ContentProviderClient provider = getClient(mDeniedUri);
|
||||
assertThat(provider).isNotNull();
|
||||
Bundle extras = new Bundle();
|
||||
extras.putParcelable(EXTRA_URI, mDeniedUri);
|
||||
assertThrows(SecurityException.class,
|
||||
() -> provider.call(METHOD_BIND, null, extras));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void callOnSubscribed_isSubscribed() throws RemoteException {
|
||||
ContentProviderClient provider = getClient(mDefaultUri);
|
||||
assertThat(provider).isNotNull();
|
||||
Bundle extras = new Bundle();
|
||||
extras.putParcelable(EXTRA_URI, mDefaultUri);
|
||||
provider.call(METHOD_SUBSCRIBE, null, extras);
|
||||
|
||||
Bundle res = provider.call(METHOD_IS_SUBSCRIBED, null, extras);
|
||||
assertThat(res).isNotNull();
|
||||
boolean isSubscribed = res.getBoolean(IS_SUBSCRIBED_KEY, false);
|
||||
assertThat(isSubscribed).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void callOnUnsubscribed_isUnsubscribed() throws RemoteException {
|
||||
ContentProviderClient provider = getClient(mDefaultUri);
|
||||
assertThat(provider).isNotNull();
|
||||
Bundle extras = new Bundle();
|
||||
extras.putParcelable(EXTRA_URI, mDefaultUri);
|
||||
provider.call(METHOD_SUBSCRIBE, null, extras);
|
||||
provider.call(METHOD_UNSUBSCRIBE, null, extras);
|
||||
|
||||
Bundle res = provider.call(METHOD_IS_SUBSCRIBED, null, extras);
|
||||
assertThat(res).isNotNull();
|
||||
boolean isSubscribed = res.getBoolean(IS_SUBSCRIBED_KEY, true);
|
||||
assertThat(isSubscribed).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void callDestroy_isDestroyed() throws RemoteException {
|
||||
ContentProviderClient provider = getClient(mDefaultUri);
|
||||
assertThat(provider).isNotNull();
|
||||
Bundle extras = new Bundle();
|
||||
extras.putParcelable(EXTRA_URI, mDefaultUri);
|
||||
provider.call(METHOD_SUBSCRIBE, null, extras);
|
||||
provider.call(METHOD_UNSUBSCRIBE, null, extras);
|
||||
provider.call(METHOD_DESTROY, null, extras);
|
||||
|
||||
Bundle res = provider.call(METHOD_IS_DESTROYED, null, extras);
|
||||
assertThat(res).isNotNull();
|
||||
boolean isDestroyed = res.getBoolean(IS_DESTROYED_KEY, false);
|
||||
assertThat(isDestroyed).isTrue();
|
||||
}
|
||||
|
||||
private ContentProviderClient getClient(Uri uri) {
|
||||
return mContext.getContentResolver().acquireContentProviderClient(uri);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.car.qc.testutils;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class AllowedTestQCProvider extends TestQCProvider {
|
||||
@Override
|
||||
protected Set<String> getAllowlistedPackages() {
|
||||
Set<String> allowlist = new HashSet<>();
|
||||
allowlist.add("com.android.car.qc.tests.unit");
|
||||
return allowlist;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.car.qc.testutils;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class DeniedTestQCProvider extends TestQCProvider {
|
||||
@Override
|
||||
protected Set<String> getAllowlistedPackages() {
|
||||
return new HashSet<>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.car.qc.testutils;
|
||||
|
||||
import android.R;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.car.qc.QCItem;
|
||||
import com.android.car.qc.QCTile;
|
||||
import com.android.car.qc.provider.BaseQCProvider;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class TestQCProvider extends BaseQCProvider {
|
||||
|
||||
public static final String METHOD_IS_SUBSCRIBED = "METHOD_IS_SUBSCRIBED";
|
||||
public static final String IS_SUBSCRIBED_KEY = "IS_SUBSCRIBED";
|
||||
public static final String METHOD_IS_DESTROYED = "METHOD_IS_DESTROYED";
|
||||
public static final String IS_DESTROYED_KEY = "IS_DESTROYED";
|
||||
|
||||
public static final String KEY_DEFAULT = "DEFAULT";
|
||||
public static final String KEY_SLOW = "SLOW";
|
||||
|
||||
private final Set<Uri> mSubscribedUris = new HashSet<>();
|
||||
private final Set<Uri> mDestroyedUris = new HashSet<>();
|
||||
|
||||
@Override
|
||||
public Bundle call(String method, String arg, Bundle extras) {
|
||||
if (METHOD_IS_SUBSCRIBED.equals(method)) {
|
||||
Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_URI));
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putBoolean(IS_SUBSCRIBED_KEY, mSubscribedUris.contains(uri));
|
||||
return bundle;
|
||||
}
|
||||
if (METHOD_IS_DESTROYED.equals(method)) {
|
||||
Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_URI));
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putBoolean(IS_DESTROYED_KEY, mDestroyedUris.contains(uri));
|
||||
return bundle;
|
||||
}
|
||||
return super.call(method, arg, extras);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected QCItem onBind(@NonNull Uri uri) {
|
||||
List<String> pathSegments = uri.getPathSegments();
|
||||
String key = pathSegments.get(0);
|
||||
|
||||
if (KEY_DEFAULT.equals(key)) {
|
||||
return new QCTile.Builder()
|
||||
.setIcon(Icon.createWithResource(getContext(), R.drawable.btn_star))
|
||||
.build();
|
||||
} else if (KEY_SLOW.equals(key)) {
|
||||
// perform a slow operation that should trigger the strict thread policy
|
||||
Drawable d = getContext().getDrawable(R.drawable.btn_star);
|
||||
Bitmap bitmap = drawableToBitmap(d);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
|
||||
byte[] b = baos.toByteArray();
|
||||
Icon icon = Icon.createWithData(b, 0, b.length);
|
||||
return new QCTile.Builder()
|
||||
.setIcon(icon)
|
||||
.build();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSubscribed(@NonNull Uri uri) {
|
||||
mSubscribedUris.add(uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onUnsubscribed(@NonNull Uri uri) {
|
||||
mSubscribedUris.remove(uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy(@NonNull Uri uri) {
|
||||
mDestroyedUris.add(uri);
|
||||
}
|
||||
|
||||
private static Bitmap drawableToBitmap(Drawable drawable) {
|
||||
|
||||
if (drawable instanceof BitmapDrawable) {
|
||||
return ((BitmapDrawable) drawable).getBitmap();
|
||||
}
|
||||
|
||||
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
|
||||
drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
|
||||
drawable.draw(canvas);
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.car.qc.view;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.testng.Assert.assertThrows;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.car.qc.QCList;
|
||||
import com.android.car.qc.QCRow;
|
||||
import com.android.dx.mockito.inline.extended.ExtendedMockito;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class QCListViewTest {
|
||||
|
||||
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||
private QCListView mView;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mView = new QCListView(mContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onChanged_null_noViews() {
|
||||
mView.onChanged(null);
|
||||
assertThat(mView.getChildCount()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onChanged_invalidType_throwsIllegalArgumentException() {
|
||||
QCRow row = new QCRow.Builder().build();
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> mView.onChanged(row));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onChanged_createsRows() {
|
||||
QCList list = new QCList.Builder()
|
||||
.addRow(new QCRow.Builder().build())
|
||||
.addRow(new QCRow.Builder().build())
|
||||
.build();
|
||||
mView.onChanged(list);
|
||||
assertThat(mView.getChildCount()).isEqualTo(2);
|
||||
assertThat(mView.getChildAt(0) instanceof QCRowView).isTrue();
|
||||
assertThat(mView.getChildAt(1) instanceof QCRowView).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onChanged_decreasedRowCount_removesExtraRows() {
|
||||
QCList list = new QCList.Builder()
|
||||
.addRow(new QCRow.Builder().build())
|
||||
.addRow(new QCRow.Builder().build())
|
||||
.build();
|
||||
mView.onChanged(list);
|
||||
assertThat(mView.getChildCount()).isEqualTo(2);
|
||||
list = new QCList.Builder()
|
||||
.addRow(new QCRow.Builder().build())
|
||||
.build();
|
||||
mView.onChanged(list);
|
||||
assertThat(mView.getChildCount()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setActionListener_setsOnChildView() {
|
||||
QCList list = new QCList.Builder()
|
||||
.addRow(new QCRow.Builder().build())
|
||||
.addRow(new QCRow.Builder().build())
|
||||
.build();
|
||||
mView.onChanged(list);
|
||||
assertThat(mView.getChildCount()).isEqualTo(2);
|
||||
QCRowView row1 = (QCRowView) mView.getChildAt(0);
|
||||
QCRowView row2 = (QCRowView) mView.getChildAt(1);
|
||||
ExtendedMockito.spyOn(row1);
|
||||
ExtendedMockito.spyOn(row2);
|
||||
QCView.QCActionListener listener = mock(QCView.QCActionListener.class);
|
||||
mView.setActionListener(listener);
|
||||
ExtendedMockito.verify(row1).setActionListener(listener);
|
||||
ExtendedMockito.verify(row2).setActionListener(listener);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.car.qc.view;
|
||||
|
||||
import static com.android.car.qc.QCItem.QC_TYPE_ACTION_SWITCH;
|
||||
import static com.android.car.qc.QCItem.QC_TYPE_ACTION_TOGGLE;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.Switch;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.test.annotation.UiThreadTest;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.car.qc.QCActionItem;
|
||||
import com.android.car.qc.QCRow;
|
||||
import com.android.car.qc.QCSlider;
|
||||
import com.android.car.qc.R;
|
||||
import com.android.dx.mockito.inline.extended.ExtendedMockito;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class QCRowViewTest {
|
||||
|
||||
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||
private QCRowView mView;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mView = new QCRowView(mContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setRow_null_notVisible() {
|
||||
mView.setRow(null);
|
||||
assertThat(mView.getVisibility()).isEqualTo(View.GONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setRow_notNull_visible() {
|
||||
QCRow row = new QCRow.Builder().build();
|
||||
mView.setRow(row);
|
||||
assertThat(mView.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setRow_setsTitle() {
|
||||
String title = "TEST_TITLE";
|
||||
QCRow row = new QCRow.Builder().setTitle(title).build();
|
||||
mView.setRow(row);
|
||||
TextView titleView = mView.findViewById(R.id.qc_title);
|
||||
assertThat(titleView.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
assertThat(titleView.getText().toString()).isEqualTo(title);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setRow_setsSubtitle() {
|
||||
String subtitle = "TEST_TITLE";
|
||||
QCRow row = new QCRow.Builder().setSubtitle(subtitle).build();
|
||||
mView.setRow(row);
|
||||
TextView subtitleView = mView.findViewById(R.id.qc_summary);
|
||||
assertThat(subtitleView.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
assertThat(subtitleView.getText().toString()).isEqualTo(subtitle);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setRow_setsIcon() {
|
||||
Icon icon = Icon.createWithResource(mContext, android.R.drawable.btn_star);
|
||||
QCRow row = new QCRow.Builder().setIcon(icon).build();
|
||||
mView.setRow(row);
|
||||
ImageView iconView = mView.findViewById(R.id.qc_icon);
|
||||
assertThat(iconView.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
assertThat(iconView.getDrawable()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void setRow_createsStartItems() {
|
||||
QCRow row = new QCRow.Builder()
|
||||
.addStartItem(new QCActionItem.Builder(QC_TYPE_ACTION_SWITCH).build())
|
||||
.addStartItem(new QCActionItem.Builder(QC_TYPE_ACTION_TOGGLE).build())
|
||||
.build();
|
||||
mView.setRow(row);
|
||||
LinearLayout startContainer = mView.findViewById(R.id.qc_row_start_items);
|
||||
assertThat(startContainer.getChildCount()).isEqualTo(2);
|
||||
assertThat((View) startContainer.getChildAt(0).findViewById(
|
||||
android.R.id.switch_widget)).isNotNull();
|
||||
assertThat((View) startContainer.getChildAt(1).findViewById(
|
||||
R.id.qc_toggle_button)).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void setRow_createsEndItems() {
|
||||
QCRow row = new QCRow.Builder()
|
||||
.addEndItem(new QCActionItem.Builder(QC_TYPE_ACTION_SWITCH).build())
|
||||
.addEndItem(new QCActionItem.Builder(QC_TYPE_ACTION_TOGGLE).build())
|
||||
.build();
|
||||
mView.setRow(row);
|
||||
LinearLayout endContainer = mView.findViewById(R.id.qc_row_end_items);
|
||||
assertThat(endContainer.getChildCount()).isEqualTo(2);
|
||||
assertThat((View) endContainer.getChildAt(0).findViewById(
|
||||
android.R.id.switch_widget)).isNotNull();
|
||||
assertThat((View) endContainer.getChildAt(1).findViewById(
|
||||
R.id.qc_toggle_button)).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setRow_noSlider_sliderViewNotVisible() {
|
||||
QCRow row = new QCRow.Builder().build();
|
||||
mView.setRow(row);
|
||||
LinearLayout sliderContainer = mView.findViewById(R.id.qc_seekbar_wrapper);
|
||||
assertThat(sliderContainer.getVisibility()).isEqualTo(View.GONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void setRow_hasSlider_sliderViewVisible() {
|
||||
QCRow row = new QCRow.Builder()
|
||||
.addSlider(new QCSlider.Builder().build())
|
||||
.build();
|
||||
mView.setRow(row);
|
||||
LinearLayout sliderContainer = mView.findViewById(R.id.qc_seekbar_wrapper);
|
||||
assertThat(sliderContainer.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onRowClick_firesAction() throws PendingIntent.CanceledException {
|
||||
PendingIntent action = mock(PendingIntent.class);
|
||||
QCRow row = new QCRow.Builder().setPrimaryAction(action).build();
|
||||
mView.setRow(row);
|
||||
mView.findViewById(R.id.qc_row_content).performClick();
|
||||
verify(action).send(any(Context.class), anyInt(), eq(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onSwitchClick_firesAction() throws PendingIntent.CanceledException {
|
||||
PendingIntent action = mock(PendingIntent.class);
|
||||
QCRow row = new QCRow.Builder()
|
||||
.addEndItem(
|
||||
new QCActionItem.Builder(QC_TYPE_ACTION_SWITCH).setAction(action).build())
|
||||
.build();
|
||||
mView.setRow(row);
|
||||
LinearLayout endContainer = mView.findViewById(R.id.qc_row_end_items);
|
||||
assertThat(endContainer.getChildCount()).isEqualTo(1);
|
||||
endContainer.getChildAt(0).performClick();
|
||||
verify(action).send(any(Context.class), anyInt(), any(Intent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void onToggleClick_firesAction() throws PendingIntent.CanceledException {
|
||||
PendingIntent action = mock(PendingIntent.class);
|
||||
QCRow row = new QCRow.Builder()
|
||||
.addEndItem(
|
||||
new QCActionItem.Builder(QC_TYPE_ACTION_TOGGLE).setAction(action).build())
|
||||
.build();
|
||||
mView.setRow(row);
|
||||
LinearLayout endContainer = mView.findViewById(R.id.qc_row_end_items);
|
||||
assertThat(endContainer.getChildCount()).isEqualTo(1);
|
||||
endContainer.getChildAt(0).performClick();
|
||||
verify(action).send(any(Context.class), anyInt(), any(Intent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void onSliderChange_firesAction() throws PendingIntent.CanceledException {
|
||||
PendingIntent action = mock(PendingIntent.class);
|
||||
QCRow row = new QCRow.Builder()
|
||||
.addSlider(new QCSlider.Builder().setInputAction(action).build())
|
||||
.build();
|
||||
mView.setRow(row);
|
||||
SeekBar seekBar = mView.findViewById(R.id.qc_seekbar);
|
||||
seekBar.setProgress(50);
|
||||
MotionEvent motionEvent = ExtendedMockito.mock(MotionEvent.class);
|
||||
ExtendedMockito.when(motionEvent.getAction()).thenReturn(MotionEvent.ACTION_UP);
|
||||
seekBar.onTouchEvent(motionEvent);
|
||||
verify(action).send(any(Context.class), anyInt(), any(Intent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void setRow_switchViewThumbTintList() {
|
||||
PendingIntent action = mock(PendingIntent.class);
|
||||
QCRow row = new QCRow.Builder()
|
||||
.addEndItem(
|
||||
new QCActionItem.Builder(QC_TYPE_ACTION_SWITCH).setAction(action).build())
|
||||
.build();
|
||||
mView.setRow(row);
|
||||
LinearLayout endContainer = mView.findViewById(R.id.qc_row_end_items);
|
||||
assertThat(endContainer.getChildCount()).isEqualTo(1);
|
||||
Switch switchView = (Switch) endContainer.getChildAt(0);
|
||||
assertThat(switchView.getThumbTintList()).isNotNull();
|
||||
|
||||
ColorStateList switchColorStateList = switchView.getThumbTintList();
|
||||
int[] enabledState = {android.R.attr.state_enabled};
|
||||
int[] disabledState = {-android.R.attr.state_enabled};
|
||||
assertThat(switchColorStateList.getColorForState(enabledState, 0)).isNotEqualTo(
|
||||
switchColorStateList.getColorForState(disabledState, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void setRow_sliderViewThumbTintList() {
|
||||
PendingIntent action = mock(PendingIntent.class);
|
||||
QCRow row = new QCRow.Builder()
|
||||
.addSlider(new QCSlider.Builder().setInputAction(action).build())
|
||||
.build();
|
||||
mView.setRow(row);
|
||||
SeekBar seekBar = mView.findViewById(R.id.qc_seekbar);
|
||||
assertThat(seekBar.getThumbTintList()).isNotNull();
|
||||
|
||||
ColorStateList seekBarColorStateList = seekBar.getThumbTintList();
|
||||
int[] enabledState = {android.R.attr.state_enabled};
|
||||
int[] disabledState = {-android.R.attr.state_enabled};
|
||||
assertThat(seekBarColorStateList.getColorForState(enabledState, 0)).isNotEqualTo(
|
||||
seekBarColorStateList.getColorForState(disabledState, 0));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.car.qc.view;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.MotionEvent;
|
||||
import android.widget.SeekBar;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class QCSeekBarViewTest {
|
||||
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||
private final QCSeekBarView mView = new QCSeekBarView(mContext);
|
||||
|
||||
@Mock
|
||||
private MotionEvent mMotionEvent;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_UP);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enabled_standardTouchEvent() {
|
||||
assertThat(mView.onTouchEvent(mMotionEvent)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void disabled_standardTouchEvent() {
|
||||
mView.setEnabled(false);
|
||||
|
||||
assertThat(mView.onTouchEvent(mMotionEvent)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clickableWhileDisabled_customTouchEvent() {
|
||||
mView.setEnabled(false);
|
||||
mView.setClickableWhileDisabled(true);
|
||||
|
||||
assertThat(mView.onTouchEvent(mMotionEvent)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clickableWhileDisabled_actionDown_doesNotTriggerDisabledClickListener() {
|
||||
AtomicBoolean called = new AtomicBoolean(false);
|
||||
Consumer<SeekBar> disabledClickListener = seekBar -> called.set(true);
|
||||
mView.setEnabled(false);
|
||||
mView.setClickableWhileDisabled(true);
|
||||
mView.setDisabledClickListener(disabledClickListener);
|
||||
when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_DOWN);
|
||||
|
||||
assertThat(mView.onTouchEvent(mMotionEvent)).isTrue();
|
||||
assertThat(called.get()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clickableWhileDisabled_actionUp_triggersDisabledClickListener() {
|
||||
AtomicBoolean called = new AtomicBoolean(false);
|
||||
Consumer<SeekBar> disabledClickListener = seekBar -> called.set(true);
|
||||
mView.setEnabled(false);
|
||||
mView.setClickableWhileDisabled(true);
|
||||
mView.setDisabledClickListener(disabledClickListener);
|
||||
|
||||
assertThat(mView.onTouchEvent(mMotionEvent)).isTrue();
|
||||
assertThat(called.get()).isTrue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.car.qc.view;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.testng.Assert.assertThrows;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.graphics.drawable.LayerDrawable;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.test.annotation.UiThreadTest;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.car.qc.QCRow;
|
||||
import com.android.car.qc.QCTile;
|
||||
import com.android.car.qc.R;
|
||||
import com.android.car.ui.uxr.DrawableStateToggleButton;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class QCTileViewTest {
|
||||
|
||||
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||
private QCTileView mView;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mView = new QCTileView(mContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onChanged_null_noViews() {
|
||||
mView.onChanged(null);
|
||||
assertThat(mView.getChildCount()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onChanged_invalidType_throwsIllegalArgumentException() {
|
||||
QCRow row = new QCRow.Builder().build();
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> mView.onChanged(row));
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void onChanged_setsSubtitleView() {
|
||||
String subtitle = "TEST_SUBTITLE";
|
||||
QCTile tile = new QCTile.Builder().setSubtitle(subtitle).build();
|
||||
mView.onChanged(tile);
|
||||
TextView subtitleView = mView.findViewById(android.R.id.summary);
|
||||
assertThat(subtitleView.getText().toString()).isEqualTo(subtitle);
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void onChanged_setsButtonState() {
|
||||
QCTile tile = new QCTile.Builder().setChecked(true).setEnabled(true).build();
|
||||
mView.onChanged(tile);
|
||||
DrawableStateToggleButton button = mView.findViewById(R.id.qc_tile_toggle_button);
|
||||
assertThat(button.isEnabled()).isTrue();
|
||||
assertThat(button.isChecked()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void onChanged_setsIcon() {
|
||||
Icon icon = Icon.createWithResource(mContext, android.R.drawable.btn_star);
|
||||
QCTile tile = new QCTile.Builder().setIcon(icon).build();
|
||||
mView.onChanged(tile);
|
||||
DrawableStateToggleButton button = mView.findViewById(R.id.qc_tile_toggle_button);
|
||||
Drawable buttonDrawable = button.getButtonDrawable();
|
||||
assertThat(buttonDrawable).isNotNull();
|
||||
assertThat(buttonDrawable instanceof LayerDrawable).isTrue();
|
||||
assertThat(((LayerDrawable) buttonDrawable).getNumberOfLayers()).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void onClick_firesAction() throws PendingIntent.CanceledException {
|
||||
PendingIntent action = mock(PendingIntent.class);
|
||||
QCTile tile = new QCTile.Builder().setChecked(false).setAction(action).build();
|
||||
mView.onChanged(tile);
|
||||
mView.findViewById(R.id.qc_tile_wrapper).performClick();
|
||||
DrawableStateToggleButton button = mView.findViewById(R.id.qc_tile_toggle_button);
|
||||
assertThat(button.isChecked()).isTrue();
|
||||
verify(action).send(any(Context.class), anyInt(), any(Intent.class));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.car.qc.view;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.testng.Assert.assertThrows;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.annotation.UiThreadTest;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.car.qc.QCList;
|
||||
import com.android.car.qc.QCRow;
|
||||
import com.android.car.qc.QCTile;
|
||||
import com.android.dx.mockito.inline.extended.ExtendedMockito;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class QCViewTest {
|
||||
|
||||
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||
private QCView mView;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mView = new QCView(mContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onChanged_null_noViews() {
|
||||
mView.onChanged(null);
|
||||
assertThat(mView.getChildCount()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onChanged_invalidType_throwsIllegalArgumentException() {
|
||||
QCRow row = new QCRow.Builder().build();
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> mView.onChanged(row));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onChanged_list_createsListView() {
|
||||
QCList list = new QCList.Builder().build();
|
||||
mView.onChanged(list);
|
||||
assertThat(mView.getChildCount()).isEqualTo(1);
|
||||
assertThat(mView.getChildAt(0) instanceof QCListView).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void onChanged_tile_createsTileView() {
|
||||
QCTile tile = new QCTile.Builder().build();
|
||||
mView.onChanged(tile);
|
||||
assertThat(mView.getChildCount()).isEqualTo(1);
|
||||
assertThat(mView.getChildAt(0) instanceof QCTileView).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void onChanged_alreadyHasView_callsOnChanged() {
|
||||
QCTile tile = new QCTile.Builder().build();
|
||||
mView.onChanged(tile);
|
||||
assertThat(mView.getChildCount()).isEqualTo(1);
|
||||
assertThat(mView.getChildAt(0) instanceof QCTileView).isTrue();
|
||||
QCTileView tileView = (QCTileView) mView.getChildAt(0);
|
||||
ExtendedMockito.spyOn(tileView);
|
||||
mView.onChanged(tile);
|
||||
verify(tileView).onChanged(tile);
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void setActionListener_setsOnChildView() {
|
||||
QCTile tile = new QCTile.Builder().build();
|
||||
mView.onChanged(tile);
|
||||
assertThat(mView.getChildCount()).isEqualTo(1);
|
||||
assertThat(mView.getChildAt(0) instanceof QCTileView).isTrue();
|
||||
QCTileView tileView = (QCTileView) mView.getChildAt(0);
|
||||
ExtendedMockito.spyOn(tileView);
|
||||
QCView.QCActionListener listener = mock(QCView.QCActionListener.class);
|
||||
mView.setActionListener(listener);
|
||||
ExtendedMockito.verify(tileView).setActionListener(listener);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user