Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ceea877d authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "[view-compiler] Add layout validation"

parents 18b925a7 3cbf176e
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -24,6 +24,9 @@ cc_defaults {
        "libdexfile",
        "slicer",
    ],
    static_libs: [
        "libtinyxml2",
    ],
}

cc_library_host_static {
@@ -32,7 +35,9 @@ cc_library_host_static {
    srcs: [
        "dex_builder.cc",
        "java_lang_builder.cc",
        "tinyxml_layout_parser.cc",
        "util.cc",
        "layout_validation.cc",
    ],
}

@@ -43,7 +48,6 @@ cc_binary_host {
        "main.cc",
    ],
    static_libs: [
        "libtinyxml2",
        "libgflags",
        "libviewcompiler",
    ],
@@ -54,6 +58,7 @@ cc_test_host {
    defaults: ["viewcompiler_defaults"],
    srcs: [
        "dex_builder_test.cc",
        "layout_validation_test.cc",
        "util_test.cc",
    ],
    static_libs: [
+42 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.
 */

#include "layout_validation.h"

#include "android-base/stringprintf.h"

namespace startop {

void LayoutValidationVisitor::VisitStartTag(const std::u16string& name) {
  if (0 == name.compare(u"merge")) {
    message_ = "Merge tags are not supported";
    can_compile_ = false;
  }
  if (0 == name.compare(u"include")) {
    message_ = "Include tags are not supported";
    can_compile_ = false;
  }
  if (0 == name.compare(u"view")) {
    message_ = "View tags are not supported";
    can_compile_ = false;
  }
  if (0 == name.compare(u"fragment")) {
    message_ = "Fragment tags are not supported";
    can_compile_ = false;
  }
}

}  // namespace startop
 No newline at end of file
+46 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.
 */

#ifndef LAYOUT_VALIDATION_H_
#define LAYOUT_VALIDATION_H_

#include "dex_builder.h"

#include <string>

namespace startop {

// This visitor determines whether a layout can be compiled. Since we do not currently support all
// features, such as includes and merges, we need to pre-validate the layout before we start
// compiling.
class LayoutValidationVisitor {
 public:
  void VisitStartDocument() const {}
  void VisitEndDocument() const {}
  void VisitStartTag(const std::u16string& name);
  void VisitEndTag() const {}

  const std::string& message() const { return message_; }
  bool can_compile() const { return can_compile_; }

 private:
  std::string message_{"Okay"};
  bool can_compile_{true};
};

}  // namespace startop

#endif  // LAYOUT_VALIDATION_H_
+163 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.
 */
#include "tinyxml_layout_parser.h"

#include "gtest/gtest.h"

using startop::CanCompileLayout;
using std::string;

namespace {
void ValidateXmlText(const string& xml, bool expected) {
  tinyxml2::XMLDocument doc;
  doc.Parse(xml.c_str());
  EXPECT_EQ(CanCompileLayout(doc), expected);
}
}  // namespace

TEST(LayoutValidationTest, SingleButtonLayout) {
  const string xml = R"(<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:text="Hello, World!">

</Button>)";
  ValidateXmlText(xml, /*expected=*/true);
}

TEST(LayoutValidationTest, SmallConstraintLayout) {
  const string xml = R"(<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/button6"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="16dp"
        android:layout_marginBottom="16dp"
        android:text="Button"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <Button
        android:id="@+id/button7"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:layout_marginBottom="16dp"
        android:text="Button2"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/button6" />

    <Button
        android:id="@+id/button8"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:layout_marginBottom="16dp"
        android:text="Button1"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/button7" />
</android.support.constraint.ConstraintLayout>)";
  ValidateXmlText(xml, /*expected=*/true);
}

TEST(LayoutValidationTest, MergeNode) {
  const string xml = R"(<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">

    <TextView
        android:id="@+id/textView3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TextView" />

    <Button
        android:id="@+id/button9"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button" />
</merge>)";
  ValidateXmlText(xml, /*expected=*/false);
}

TEST(LayoutValidationTest, IncludeLayout) {
  const string xml = R"(<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <include
        layout="@layout/single_button_layout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>)";
  ValidateXmlText(xml, /*expected=*/false);
}

TEST(LayoutValidationTest, ViewNode) {
  const string xml = R"(<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <view
        class="android.support.design.button.MaterialButton"
        id="@+id/view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>)";
  ValidateXmlText(xml, /*expected=*/false);
}

TEST(LayoutValidationTest, FragmentNode) {
  // This test case is from https://developer.android.com/guide/components/fragments
  const string xml = R"(<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <fragment android:name="com.example.news.ArticleListFragment"
            android:id="@+id/list"
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="match_parent" />
    <fragment android:name="com.example.news.ArticleReaderFragment"
            android:id="@+id/viewer"
            android:layout_weight="2"
            android:layout_width="0dp"
            android:layout_height="match_parent" />
</LinearLayout>)";
  ValidateXmlText(xml, /*expected=*/false);
}
+7 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@

#include "dex_builder.h"
#include "java_lang_builder.h"
#include "tinyxml_layout_parser.h"
#include "util.h"

#include "tinyxml2.h"
@@ -100,6 +101,12 @@ int main(int argc, char** argv) {
  XMLDocument xml;
  xml.LoadFile(filename);

  string message{};
  if (!startop::CanCompileLayout(xml, &message)) {
    LOG(ERROR) << "Layout not supported: " << message;
    return 1;
  }

  std::ofstream outfile;
  if (FLAGS_out != kStdoutFilename) {
    outfile.open(FLAGS_out);
Loading