| /* |
| * Copyright (C) 2023 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.internal.systemui.lint |
| |
| import com.android.tools.lint.checks.infrastructure.TestFiles |
| import com.android.tools.lint.checks.infrastructure.TestMode |
| import com.android.tools.lint.detector.api.Detector |
| import com.android.tools.lint.detector.api.Issue |
| import org.junit.Ignore |
| import org.junit.Test |
| |
| @Suppress("UnstableApiUsage") |
| @Ignore("b/254533331") |
| class CleanArchitectureDependencyViolationDetectorTest : SystemUILintDetectorTest() { |
| override fun getDetector(): Detector { |
| return CleanArchitectureDependencyViolationDetector() |
| } |
| |
| override fun getIssues(): List<Issue> { |
| return listOf( |
| CleanArchitectureDependencyViolationDetector.ISSUE, |
| ) |
| } |
| |
| @Test |
| fun `No violations`() { |
| lint() |
| .files( |
| *LEGITIMATE_FILES, |
| ) |
| .issues( |
| CleanArchitectureDependencyViolationDetector.ISSUE, |
| ) |
| .run() |
| .expectWarningCount(0) |
| } |
| |
| @Test |
| fun `Violation - domain depends on ui`() { |
| lint() |
| .files( |
| *LEGITIMATE_FILES, |
| TestFiles.kotlin( |
| """ |
| package test.domain.interactor |
| |
| import test.ui.viewmodel.ViewModel |
| |
| class BadClass( |
| private val viewModel: ViewModel, |
| ) |
| """ |
| .trimIndent() |
| ) |
| ) |
| .issues( |
| CleanArchitectureDependencyViolationDetector.ISSUE, |
| ) |
| .testModes(TestMode.DEFAULT) |
| .run() |
| .expectWarningCount(1) |
| .expect( |
| expectedText = |
| """ |
| src/test/domain/interactor/BadClass.kt:3: Warning: The domain layer may not depend on the ui layer. [CleanArchitectureDependencyViolation] |
| import test.ui.viewmodel.ViewModel |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 0 errors, 1 warnings |
| """, |
| ) |
| } |
| |
| @Test |
| fun `Violation - ui depends on data`() { |
| lint() |
| .files( |
| *LEGITIMATE_FILES, |
| TestFiles.kotlin( |
| """ |
| package test.ui.viewmodel |
| |
| import test.data.repository.Repository |
| |
| class BadClass( |
| private val repository: Repository, |
| ) |
| """ |
| .trimIndent() |
| ) |
| ) |
| .issues( |
| CleanArchitectureDependencyViolationDetector.ISSUE, |
| ) |
| .testModes(TestMode.DEFAULT) |
| .run() |
| .expectWarningCount(1) |
| .expect( |
| expectedText = |
| """ |
| src/test/ui/viewmodel/BadClass.kt:3: Warning: The ui layer may not depend on the data layer. [CleanArchitectureDependencyViolation] |
| import test.data.repository.Repository |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 0 errors, 1 warnings |
| """, |
| ) |
| } |
| |
| @Test |
| fun `Violation - shared depends on all other layers`() { |
| lint() |
| .files( |
| *LEGITIMATE_FILES, |
| TestFiles.kotlin( |
| """ |
| package test.shared.model |
| |
| import test.data.repository.Repository |
| import test.domain.interactor.Interactor |
| import test.ui.viewmodel.ViewModel |
| |
| class BadClass( |
| private val repository: Repository, |
| private val interactor: Interactor, |
| private val viewmodel: ViewModel, |
| ) |
| """ |
| .trimIndent() |
| ) |
| ) |
| .issues( |
| CleanArchitectureDependencyViolationDetector.ISSUE, |
| ) |
| .testModes(TestMode.DEFAULT) |
| .run() |
| .expectWarningCount(3) |
| .expect( |
| expectedText = |
| """ |
| src/test/shared/model/BadClass.kt:3: Warning: The shared layer may not depend on the data layer. [CleanArchitectureDependencyViolation] |
| import test.data.repository.Repository |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| src/test/shared/model/BadClass.kt:4: Warning: The shared layer may not depend on the domain layer. [CleanArchitectureDependencyViolation] |
| import test.domain.interactor.Interactor |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| src/test/shared/model/BadClass.kt:5: Warning: The shared layer may not depend on the ui layer. [CleanArchitectureDependencyViolation] |
| import test.ui.viewmodel.ViewModel |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 0 errors, 3 warnings |
| """, |
| ) |
| } |
| |
| @Test |
| fun `Violation - data depends on domain`() { |
| lint() |
| .files( |
| *LEGITIMATE_FILES, |
| TestFiles.kotlin( |
| """ |
| package test.data.repository |
| |
| import test.domain.interactor.Interactor |
| |
| class BadClass( |
| private val interactor: Interactor, |
| ) |
| """ |
| .trimIndent() |
| ) |
| ) |
| .issues( |
| CleanArchitectureDependencyViolationDetector.ISSUE, |
| ) |
| .testModes(TestMode.DEFAULT) |
| .run() |
| .expectWarningCount(1) |
| .expect( |
| expectedText = |
| """ |
| src/test/data/repository/BadClass.kt:3: Warning: The data layer may not depend on the domain layer. [CleanArchitectureDependencyViolation] |
| import test.domain.interactor.Interactor |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 0 errors, 1 warnings |
| """, |
| ) |
| } |
| |
| companion object { |
| private val MODEL_FILE = |
| TestFiles.kotlin( |
| """ |
| package test.shared.model |
| |
| import test.some.other.thing.SomeOtherThing |
| |
| data class Model( |
| private val name: String, |
| ) |
| """ |
| .trimIndent() |
| ) |
| private val REPOSITORY_FILE = |
| TestFiles.kotlin( |
| """ |
| package test.data.repository |
| |
| import test.shared.model.Model |
| import test.some.other.thing.SomeOtherThing |
| |
| class Repository { |
| private val models = listOf( |
| Model("one"), |
| Model("two"), |
| Model("three"), |
| ) |
| |
| fun getModels(): List<Model> { |
| return models |
| } |
| } |
| """ |
| .trimIndent() |
| ) |
| private val INTERACTOR_FILE = |
| TestFiles.kotlin( |
| """ |
| package test.domain.interactor |
| |
| import test.data.repository.Repository |
| import test.shared.model.Model |
| |
| class Interactor( |
| private val repository: Repository, |
| ) { |
| fun getModels(): List<Model> { |
| return repository.getModels() |
| } |
| } |
| """ |
| .trimIndent() |
| ) |
| private val VIEW_MODEL_FILE = |
| TestFiles.kotlin( |
| """ |
| package test.ui.viewmodel |
| |
| import test.domain.interactor.Interactor |
| import test.some.other.thing.SomeOtherThing |
| |
| class ViewModel( |
| private val interactor: Interactor, |
| ) { |
| fun getNames(): List<String> { |
| return interactor.getModels().map { model -> model.name } |
| } |
| } |
| """ |
| .trimIndent() |
| ) |
| private val NON_CLEAN_ARCHITECTURE_FILE = |
| TestFiles.kotlin( |
| """ |
| package test.some.other.thing |
| |
| import test.data.repository.Repository |
| import test.domain.interactor.Interactor |
| import test.ui.viewmodel.ViewModel |
| |
| class SomeOtherThing { |
| init { |
| val viewModel = ViewModel( |
| interactor = Interactor( |
| repository = Repository(), |
| ), |
| ) |
| } |
| } |
| """ |
| .trimIndent() |
| ) |
| private val LEGITIMATE_FILES = |
| arrayOf( |
| MODEL_FILE, |
| REPOSITORY_FILE, |
| INTERACTOR_FILE, |
| VIEW_MODEL_FILE, |
| NON_CLEAN_ARCHITECTURE_FILE, |
| ) |
| } |
| } |