Test all attribute adapters

This commit is contained in:
Akash Yadav
2022-11-12 22:42:27 +05:30
parent 3720cce42d
commit dd086d7f14
8 changed files with 187 additions and 58 deletions
Binary file not shown.
@@ -0,0 +1,6 @@
<AbsoluteLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/template_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
@@ -43,8 +43,10 @@ import com.itsaky.androidide.inflater.internal.utils.IDTable
import com.itsaky.androidide.projects.ProjectManager
import com.itsaky.androidide.projects.api.AndroidModule
import com.itsaky.androidide.utils.ILogger
import com.itsaky.androidide.utils.VMUtils
import com.itsaky.androidide.xml.widgets.Widget
import com.itsaky.androidide.xml.widgets.WidgetTable
import org.jetbrains.annotations.TestOnly
import java.io.File
import java.lang.reflect.Method
@@ -64,7 +66,7 @@ open class LayoutInflaterImpl : ILayoutInflater() {
get() = this._primaryInflatingFile!!
protected val currentLayoutFile: LayoutFile
get() = _currentLayoutFile!!
get() = this._currentLayoutFile!!
override fun inflate(file: File, parent: ViewGroup): IView? {
this._primaryInflatingFile = file
@@ -167,11 +169,11 @@ open class LayoutInflaterImpl : ILayoutInflater() {
val adapter =
AttributeAdapterIndex.getAdapter(view.name)
?: throw InflateException("No attribute adapter found for view ${view.name}")
adapter.applyBasic(view)
view.view.layoutParams = generateLayoutParams(parentView)
parent.addChild(view)
adapter.applyBasic(view)
if (element.childCount > 0 && view is IViewGroup) {
for (child in element.childList) {
@@ -35,7 +35,7 @@ constructor(
override val tag: String = name.tagName()
) : IView {
private val log = ILogger.newInstance(javaClass.simpleName)
protected val attributes = mutableListOf<IAttribute>()
internal val attributes = mutableListOf<IAttribute>()
override var parent: IViewGroup? = null
@@ -76,7 +76,7 @@ open class ViewAttrAdapter : IAttributeAdapter() {
"foregroundGravity" -> foregroundGravity = parseGravity(value)
"foregroundTint" -> foregroundTintList = parseColorStateList(context, value)
"foregroundTintMode" -> foregroundTintMode = parsePorterDuffMode(value)
"id" -> parseId(file.resName, value)
"id" -> id = parseId(file.resName, value)
"minHeight" -> minimumWidth = parseDimension(context, value)
"minWidth" -> minimumHeight = parseDimension(context, value)
"padding" ->
@@ -51,59 +51,21 @@ import com.itsaky.androidide.inflater.internal.adapters.WebViewAttrAdapter
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import java.io.File
/** @author Akash Yadav */
@RunWith(RobolectricTestRunner::class)
class AdapterIndexText {
@Test
fun `verify instances of adapters`() {
val expected =
mapOf(
AbsoluteLayout::class to AbsoluteLayoutAttrAdapter::class,
AutoCompleteTextView::class to AutoCompleteTextViewAttrAdapter::class,
Button::class to ButtonAttrAdapter::class,
CheckBox::class to CheckBoxAttrAdapter::class,
CheckedTextView::class to CheckedTextViewAttrAdapter::class,
EditText::class to EditTextAttrAdapter::class,
FrameLayout::class to FrameLayoutAttrAdapter::class,
GestureOverlayView::class to GestureOverlayViewAttrAdapter::class,
GridLayout::class to GridLayoutAttrAdapter::class,
ImageButton::class to ImageButtonAttrAdapter::class,
ImageView::class to ImageViewAttrAdapter::class,
LinearLayout::class to LinearLayoutAttrAdapter::class,
ListView::class to ListViewAttrAdapter::class,
ProgressBar::class to ProgressBarAttrAdapter::class,
RadioButton::class to RadioButtonAttrAdapter::class,
RelativeLayout::class to RelativeLayoutAttrAdapter::class,
SeekBar::class to SeekBarAttrAdapter::class,
Spinner::class to SpinnerAttrAdapter::class,
Switch::class to SwitchAttrAdapter::class,
SurfaceView::class to SurfaceViewAttrAdapter::class,
TextView::class to TextViewAttrAdapter::class,
TextureView::class to TextureViewAttrAdapter::class,
ToggleButton::class to ToggleButtonAttrAdapter::class,
View::class to ViewAttrAdapter::class,
WebView::class to WebViewAttrAdapter::class
)
val unexpected =
listOf(
AbsListView::class,
AbsSeekBar::class,
AbsSpinner::class,
AdapterView::class,
CompoundButton::class,
ViewGroup::class
)
expected.forEach {
viewToAdapter.forEach {
AttributeAdapterIndex.getAdapter(it.key.qualifiedName).apply {
assertThat(this).isNotNull()
assertThat(this).isInstanceOf(it.value.java)
}
}
unexpected.forEach { assertThat(AttributeAdapterIndex.getAdapter(it.qualifiedName)).isNull() }
abstractViews.forEach { assertThat(AttributeAdapterIndex.getAdapter(it.qualifiedName)).isNull() }
}
}
@@ -19,25 +19,22 @@ package com.itsaky.androidide.inflater
import android.view.View
import android.widget.LinearLayout
import androidx.appcompat.app.AppCompatActivity
import com.google.common.truth.Truth.assertThat
import com.itsaky.androidide.lookup.Lookup
import com.itsaky.androidide.inflater.internal.AttributeImpl
import com.itsaky.androidide.inflater.internal.LayoutInflaterImpl
import com.itsaky.androidide.inflater.internal.ViewImpl
import com.itsaky.androidide.inflater.internal.utils.IDTable
import com.itsaky.androidide.projects.ProjectManager
import com.itsaky.androidide.projects.builder.BuildService
import com.itsaky.androidide.tooling.api.messages.InitializeProjectMessage
import com.itsaky.androidide.tooling.testing.ToolingApiTestLauncher
import com.itsaky.androidide.projects.api.AndroidModule
import java.io.File
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.Robolectric
import org.robolectric.RobolectricTestRunner
import org.robolectric.android.controller.ActivityController
/** @author Akash Yadav */
@RunWith(RobolectricTestRunner::class)
class LayoutInflaterTest {
@Test
fun `test functionality`() {
inflaterTest {
@@ -53,7 +50,7 @@ class LayoutInflaterTest {
}
}
}
@Test
fun `test inlined layout`() {
inflaterTest {
@@ -70,8 +67,46 @@ class LayoutInflaterTest {
}
}
@Test
fun `test inflated view is of expected type`() {
inflaterTest { module ->
requiresActivity { activity ->
val parent = LinearLayout(activity)
val inflater = ILayoutInflater.newInflater() as LayoutInflaterImpl
viewToAdapter.keys.forEach { view ->
parent.removeAllViews()
module.createLayoutFile(view.simpleName!!) { file ->
file.writeText(viewDeclTemplate(view.simpleName!!))
val inflated = inflater.inflate(file, parent)
assertThat(inflated!!.view).isInstanceOf(view.java)
assertThat(inflated.view.id)
.isEqualTo(IDTable.get(view.simpleName!!, "template_view"))
(inflated as ViewImpl).attributes.apply {
assertThat(this)
.contains(AttributeImpl(INamespace.ANDROID, "id", "@+id/template_view"))
assertThat(this)
.contains(AttributeImpl(INamespace.ANDROID, "layout_height", "match_parent"))
assertThat(this)
.contains(AttributeImpl(INamespace.ANDROID, "layout_width", "match_parent"))
}
}
}
}
}
}
private fun layoutFile(name: String): File {
val app = ProjectManager.app ?: throw IllegalStateException("Project is not initialized")
return File(app.projectDir, "src/main/res/layout/$name.xml")
}
private fun AndroidModule.createLayoutFile(name: String, block: (File) -> Unit = {}): File {
return File(projectDir, "src/main/res/layout/$name.xml").apply {
if (exists()) {
delete()
}
block(this)
}
}
}
@@ -0,0 +1,124 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.inflater
import android.gesture.GestureOverlayView
import android.view.SurfaceView
import android.view.TextureView
import android.view.View
import android.view.ViewGroup
import android.webkit.WebView
import android.widget.AbsListView
import android.widget.AbsSeekBar
import android.widget.AbsSpinner
import android.widget.AbsoluteLayout
import android.widget.AdapterView
import android.widget.AutoCompleteTextView
import android.widget.Button
import android.widget.CheckBox
import android.widget.CheckedTextView
import android.widget.CompoundButton
import android.widget.EditText
import android.widget.FrameLayout
import android.widget.GridLayout
import android.widget.ImageButton
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.ListView
import android.widget.ProgressBar
import android.widget.RadioButton
import android.widget.RelativeLayout
import android.widget.SeekBar
import android.widget.Spinner
import android.widget.Switch
import android.widget.TextView
import android.widget.ToggleButton
import com.itsaky.androidide.inflater.internal.adapters.AbsoluteLayoutAttrAdapter
import com.itsaky.androidide.inflater.internal.adapters.AutoCompleteTextViewAttrAdapter
import com.itsaky.androidide.inflater.internal.adapters.ButtonAttrAdapter
import com.itsaky.androidide.inflater.internal.adapters.CheckBoxAttrAdapter
import com.itsaky.androidide.inflater.internal.adapters.CheckedTextViewAttrAdapter
import com.itsaky.androidide.inflater.internal.adapters.EditTextAttrAdapter
import com.itsaky.androidide.inflater.internal.adapters.FrameLayoutAttrAdapter
import com.itsaky.androidide.inflater.internal.adapters.GestureOverlayViewAttrAdapter
import com.itsaky.androidide.inflater.internal.adapters.GridLayoutAttrAdapter
import com.itsaky.androidide.inflater.internal.adapters.ImageButtonAttrAdapter
import com.itsaky.androidide.inflater.internal.adapters.ImageViewAttrAdapter
import com.itsaky.androidide.inflater.internal.adapters.LinearLayoutAttrAdapter
import com.itsaky.androidide.inflater.internal.adapters.ListViewAttrAdapter
import com.itsaky.androidide.inflater.internal.adapters.ProgressBarAttrAdapter
import com.itsaky.androidide.inflater.internal.adapters.RadioButtonAttrAdapter
import com.itsaky.androidide.inflater.internal.adapters.RelativeLayoutAttrAdapter
import com.itsaky.androidide.inflater.internal.adapters.SeekBarAttrAdapter
import com.itsaky.androidide.inflater.internal.adapters.SpinnerAttrAdapter
import com.itsaky.androidide.inflater.internal.adapters.SurfaceViewAttrAdapter
import com.itsaky.androidide.inflater.internal.adapters.SwitchAttrAdapter
import com.itsaky.androidide.inflater.internal.adapters.TextViewAttrAdapter
import com.itsaky.androidide.inflater.internal.adapters.TextureViewAttrAdapter
import com.itsaky.androidide.inflater.internal.adapters.ToggleButtonAttrAdapter
import com.itsaky.androidide.inflater.internal.adapters.ViewAttrAdapter
import com.itsaky.androidide.inflater.internal.adapters.WebViewAttrAdapter
internal fun viewDeclTemplate(name: String) : String =
"""
<$name
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/template_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
""".trimIndent()
internal val viewToAdapter =
mapOf(
AbsoluteLayout::class to AbsoluteLayoutAttrAdapter::class,
AutoCompleteTextView::class to AutoCompleteTextViewAttrAdapter::class,
Button::class to ButtonAttrAdapter::class,
CheckBox::class to CheckBoxAttrAdapter::class,
CheckedTextView::class to CheckedTextViewAttrAdapter::class,
EditText::class to EditTextAttrAdapter::class,
FrameLayout::class to FrameLayoutAttrAdapter::class,
GestureOverlayView::class to GestureOverlayViewAttrAdapter::class,
GridLayout::class to GridLayoutAttrAdapter::class,
ImageButton::class to ImageButtonAttrAdapter::class,
ImageView::class to ImageViewAttrAdapter::class,
LinearLayout::class to LinearLayoutAttrAdapter::class,
ListView::class to ListViewAttrAdapter::class,
ProgressBar::class to ProgressBarAttrAdapter::class,
RadioButton::class to RadioButtonAttrAdapter::class,
RelativeLayout::class to RelativeLayoutAttrAdapter::class,
SeekBar::class to SeekBarAttrAdapter::class,
Spinner::class to SpinnerAttrAdapter::class,
Switch::class to SwitchAttrAdapter::class,
SurfaceView::class to SurfaceViewAttrAdapter::class,
TextView::class to TextViewAttrAdapter::class,
TextureView::class to TextureViewAttrAdapter::class,
ToggleButton::class to ToggleButtonAttrAdapter::class,
View::class to ViewAttrAdapter::class,
WebView::class to WebViewAttrAdapter::class
)
internal val abstractViews =
listOf(
AbsListView::class,
AbsSeekBar::class,
AbsSpinner::class,
AdapterView::class,
CompoundButton::class,
ViewGroup::class
)