Add kml parser unit test

Test: atest -c cvd_import_locations_test
Bug: 230515575
Refernce : https://android.googlesource.com/platform/external/qemu/+/9846bd7/android/android-emu/android/gps/KmlParser_unittest.cpp

Change-Id: I455702bcc9b48d239e3839a0aa95842c402160cd
diff --git a/host/commands/cvd_import_locations/Android.bp b/host/commands/cvd_import_locations/Android.bp
index e0b64ff..8e81722 100644
--- a/host/commands/cvd_import_locations/Android.bp
+++ b/host/commands/cvd_import_locations/Android.bp
@@ -58,3 +58,27 @@
     ],
     defaults: ["cvd_import_locations_defaults"],
 }
+
+cc_test_host {
+    name: "cvd_import_locations_test",
+    shared_libs: [
+        "libext2_blkid",
+        "libbase",
+        "libcuttlefish_fs",
+        "libcuttlefish_utils",
+    ],
+    static_libs: [
+        "libxml2",
+        "liblocation",
+        "libgmock",
+    ],
+    srcs: [
+        "unittest/main_test.cc",
+        "unittest/kml_parser_test.cc",
+    ],
+    cflags: [
+        "-Wno-unused-parameter",
+        "-D_XOPEN_SOURCE",
+    ],
+    defaults: ["cvd_import_locations_defaults"],
+}
\ No newline at end of file
diff --git a/host/commands/cvd_import_locations/unittest/kml_parser_test.cc b/host/commands/cvd_import_locations/unittest/kml_parser_test.cc
new file mode 100644
index 0000000..51b8d03
--- /dev/null
+++ b/host/commands/cvd_import_locations/unittest/kml_parser_test.cc
@@ -0,0 +1,507 @@
+/*
+ * Copyright (C) 2015-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.
+ */
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+#include <fstream>
+#include "host/libs/location/KmlParser.h"
+
+namespace cuttlefish {
+namespace {
+bool ParseKmlData(GpsFixArray* locations, char* text, std::string* error) {
+  bool result;
+  TemporaryDir myDir;
+  std::string path = std::string(myDir.path) + "/" + "test.kml";
+
+  std::ofstream myfile;
+  myfile.open(path.c_str());
+  myfile << text;
+  myfile.close();
+  result = KmlParser::parseFile(path.c_str(), locations, error);
+  return result;
+}
+}  // namespace
+
+TEST(KmlParser, ParseNonexistentFile) {
+  GpsFixArray locations;
+  std::string error;
+  ASSERT_FALSE(KmlParser::parseFile("", &locations, &error));
+  ASSERT_EQ(0U, locations.size());
+  EXPECT_EQ(std::string("KML document not parsed successfully."), error);
+}
+
+TEST(KmlParser, ParseEmptyFile) {
+  {
+    char text[] =
+        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+        "<kml xmlns=\"http://earth.google.com/kml/2.x\">"
+        "</kml>";
+
+    GpsFixArray locations;
+    std::string error;
+    ASSERT_TRUE(ParseKmlData(&locations, text, &error));
+    EXPECT_EQ(0U, locations.size());
+    EXPECT_EQ("", error);
+  }  // destructor removes temp directory and all files under it.
+}
+
+TEST(KmlParser, ParseValidFile) {
+  {
+    char text[] =
+        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+        "<kml xmlns=\"http://earth.google.com/kml/2.x\">"
+        "<Placemark>"
+        "<name>Simple placemark</name>"
+        "<description>Attached to the ground.</description>"
+        "<Point>"
+        "<coordinates>-122.0822035425683,37.42228990140251,0</coordinates>"
+        "</Point>"
+        "</Placemark>"
+        "</kml>";
+
+    GpsFixArray locations;
+    std::string error;
+
+    ASSERT_TRUE(ParseKmlData(&locations, text, &error));
+    EXPECT_EQ(1U, locations.size());
+    EXPECT_EQ("", error);
+  }  // destructor removes temp directory and all files under it.
+}
+
+TEST(KmlParser, ParseValidComplexFile) {
+  {
+    char text[] =
+        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+        "<kml xmlns=\"http://earth.google.com/kml/2.x\">"
+        "<Document>"
+        "<name>KML Samples</name>"
+        "<Style id=\"globeIcon\">"
+        "<IconStyle></IconStyle><LineStyle><width>2</width></LineStyle>"
+        "</Style>"
+        "<Folder>"
+        "<name>Placemarks</name>"
+        "<description>These are just some</description>"
+        "<LookAt>"
+        "<tilt>40.5575073395506</tilt><range>500.6566641072245</range>"
+        "</LookAt>"
+        "<Placemark>"
+        "<name>Tessellated</name>"
+        "<visibility>0</visibility>"
+        "<description>Black line (10 pixels wide), height tracks "
+        "terrain</description>"
+        "<LookAt><longitude>-122.0839597145766</longitude></LookAt>"
+        "<styleUrl>#downArrowIcon</styleUrl>"
+        "<Point>"
+        "<altitudeMode>relativeToGround</altitudeMode>"
+        "<coordinates>-122.084075,37.4220033612141,50</coordinates>"
+        "</Point>"
+        "</Placemark>"
+        "<Placemark>"
+        "<name>Transparent</name>"
+        "<visibility>0</visibility>"
+        "<styleUrl>#transRedPoly</styleUrl>"
+        "<Polygon>"
+        "<extrude>1</extrude>"
+        "<altitudeMode>relativeToGround</altitudeMode>"
+        "<outerBoundaryIs>"
+        "<LinearRing>"
+        "<coordinates> -122.084075,37.4220033612141,50</coordinates>"
+        "</LinearRing>"
+        "</outerBoundaryIs>"
+        "</Polygon>"
+        "</Placemark>"
+        "</Folder>"
+        "<Placemark>"
+        "<name>Fruity</name>"
+        "<visibility>0</visibility>"
+        "<description><![CDATA[If the <tessellate> tag has a value of "
+        "n]]></description>"
+        "<LookAt><longitude>-112.0822680013139</longitude></LookAt>"
+        "<LineString>"
+        "<tessellate>1</tessellate>"
+        "<coordinates> -122.084075,37.4220033612141,50 </coordinates>"
+        "</LineString>"
+        "</Placemark>"
+        "</Document>"
+        "</kml>";
+
+    GpsFixArray locations;
+    std::string error;
+    ASSERT_TRUE(ParseKmlData(&locations, text, &error));
+
+    EXPECT_EQ("", error);
+    EXPECT_EQ(3U, locations.size());
+
+    EXPECT_EQ("Tessellated", locations[0].name);
+    EXPECT_EQ("Black line (10 pixels wide), height tracks terrain",
+              locations[0].description);
+    EXPECT_EQ("Transparent", locations[1].name);
+    EXPECT_EQ("", locations[1].description);
+    EXPECT_EQ("Fruity", locations[2].name);
+    EXPECT_EQ("If the <tessellate> tag has a value of n",
+              locations[2].description);
+
+    for (unsigned i = 0; i < locations.size(); ++i) {
+      EXPECT_FLOAT_EQ(-122.084075, locations[i].longitude);
+      EXPECT_FLOAT_EQ(37.4220033612141, locations[i].latitude);
+      EXPECT_FLOAT_EQ(50, locations[i].elevation);
+    }
+  }  // destructor removes temp directory and all files under it.
+}
+
+TEST(KmlParser, ParseOneCoordinate) {
+  char text[] =
+      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+      "<kml xmlns=\"http://earth.google.com/kml/2.x\">"
+      "<Placemark>"
+      "<Point>"
+      "<coordinates>-122.0822035425683,37.42228990140251,0</coordinates>"
+      "</Point>"
+      "</Placemark>"
+      "</kml>";
+
+  GpsFixArray locations;
+  std::string error;
+  ASSERT_TRUE(ParseKmlData(&locations, text, &error));
+
+  EXPECT_EQ("", error);
+  EXPECT_EQ(1U, locations.size());
+  EXPECT_FLOAT_EQ(-122.0822035425683, locations[0].longitude);
+  EXPECT_FLOAT_EQ(37.42228990140251, locations[0].latitude);
+  EXPECT_FLOAT_EQ(0.0, locations[0].elevation);
+}
+
+TEST(KmlParser, ParseMultipleCoordinates) {
+  char text[] =
+      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+      "<kml xmlns=\"http://earth.google.com/kml/2.x\">"
+      "<Placemark>"
+      "<LineString>"
+      "<coordinates>-122.0822035425683,37.42228990140251,0 \
+                    10.4,39.,20\t\t0,21.4,1"
+      "</coordinates>"
+      "</LineString>"
+      "</Placemark>"
+      "</kml>";
+  ;
+
+  GpsFixArray locations;
+  std::string error;
+  ASSERT_TRUE(ParseKmlData(&locations, text, &error));
+
+  EXPECT_EQ("", error);
+  ASSERT_EQ(3U, locations.size());
+
+  EXPECT_FLOAT_EQ(-122.0822035425683, locations[0].longitude);
+  EXPECT_FLOAT_EQ(37.42228990140251, locations[0].latitude);
+  EXPECT_FLOAT_EQ(0, locations[0].elevation);
+  EXPECT_FLOAT_EQ(10.4, locations[1].longitude);
+  EXPECT_FLOAT_EQ(39., locations[1].latitude);
+  EXPECT_FLOAT_EQ(20, locations[1].elevation);
+  EXPECT_FLOAT_EQ(0, locations[2].longitude);
+  EXPECT_FLOAT_EQ(21.4, locations[2].latitude);
+  EXPECT_FLOAT_EQ(1, locations[2].elevation);
+}
+
+TEST(KmlParser, ParseBadCoordinates) {
+  char text[] =
+      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+      "<kml xmlns=\"http://earth.google.com/kml/2.x\">"
+      "<Placemark>"
+      "<LineString>"
+      "<coordinates>-122.0822035425683, 37.42228990140251, 0 \
+                    10.4,39.20\t021.41"
+      "</coordinates>"
+      "</LineString>"
+      "</Placemark>"
+      "</kml>";
+
+  GpsFixArray locations;
+  std::string error;
+  ASSERT_FALSE(ParseKmlData(&locations, text, &error));
+}
+
+TEST(KmlParser, ParseLocationNormal) {
+  char text[] =
+      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+      "<kml xmlns=\"http://earth.google.com/kml/2.x\">"
+      "<Placemark>"
+      "<name>Simple placemark</name>"
+      "<description>Attached to the ground.</description>"
+      "<Point>"
+      "<coordinates>-122.0822035425683,37.42228990140251,0</coordinates>"
+      "</Point>"
+      "</Placemark>"
+      "</kml>";
+
+  GpsFixArray locations;
+  std::string error;
+
+  ASSERT_TRUE(ParseKmlData(&locations, text, &error));
+
+  for (unsigned i = 0; i < locations.size(); ++i) {
+    EXPECT_EQ("Simple placemark", locations[i].name);
+    EXPECT_EQ("Attached to the ground.", locations[i].description);
+    EXPECT_FLOAT_EQ(-122.0822035425683, locations[i].longitude);
+    EXPECT_FLOAT_EQ(37.42228990140251, locations[i].latitude);
+    EXPECT_FLOAT_EQ(0, locations[i].elevation);
+  }
+}
+
+TEST(KmlParser, ParseLocationNormalMissingOptionalFields) {
+  char text[] =
+      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+      "<kml xmlns=\"http://earth.google.com/kml/2.x\">"
+      "<Placemark>"
+      "<Point>"
+      "<coordinates>-122.0822035425683,37.42228990140251,0</coordinates>"
+      "</Point>"
+      "</Placemark>"
+      "</kml>";
+
+  GpsFixArray locations;
+  std::string error;
+
+  ASSERT_TRUE(ParseKmlData(&locations, text, &error));
+  EXPECT_EQ("", error);
+  ASSERT_EQ(1U, locations.size());
+
+  for (unsigned i = 0; i < locations.size(); ++i) {
+    EXPECT_EQ("", locations[i].name);
+    EXPECT_EQ("", locations[i].description);
+    EXPECT_FLOAT_EQ(-122.0822035425683, locations[i].longitude);
+    EXPECT_FLOAT_EQ(37.42228990140251, locations[i].latitude);
+    EXPECT_FLOAT_EQ(0, locations[i].elevation);
+  }
+}
+
+TEST(KmlParser, ParseLocationMissingRequiredFields) {
+  char text[] =
+      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+      "<kml xmlns=\"http://earth.google.com/kml/2.x\">"
+      "<Placemark>"
+      "<name>Simple placemark</name>"
+      "<description>Attached to the ground.</description>"
+      "</Placemark>"
+      "</kml>";
+
+  GpsFixArray locations;
+  std::string error;
+
+  ASSERT_FALSE(ParseKmlData(&locations, text, &error));
+  EXPECT_EQ("Location found with missing or malformed coordinates", error);
+}
+
+TEST(KmlParser, ParseLocationNameOnlyFirst) {
+  char text[] =
+      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+      "<kml xmlns=\"http://earth.google.com/kml/2.x\">"
+      "<Placemark>"
+      "<name>Simple placemark</name>kk0"
+      "<description>Attached to the ground.</description>"
+      "<LineString>"
+      "<coordinates>-122.0822035425683,37.42228990140251,0\
+                    -122.0822035425683,37.42228990140251,0</coordinates>"
+      "</LineString>"
+      "</Placemark>"
+      "</kml>";
+
+  GpsFixArray locations;
+  std::string error;
+  EXPECT_TRUE(ParseKmlData(&locations, text, &error));
+  EXPECT_EQ("", error);
+  ASSERT_EQ(2U, locations.size());
+
+  EXPECT_EQ("Simple placemark", locations[0].name);
+  EXPECT_EQ("Attached to the ground.", locations[0].description);
+  EXPECT_EQ("", locations[1].name);
+  EXPECT_EQ("", locations[1].description);
+
+  for (unsigned i = 0; i < locations.size(); ++i) {
+    EXPECT_FLOAT_EQ(-122.0822035425683, locations[i].longitude);
+    EXPECT_FLOAT_EQ(37.42228990140251, locations[i].latitude);
+    EXPECT_FLOAT_EQ(0, locations[i].elevation);
+  }
+}
+
+TEST(KmlParser, ParseMultipleLocations) {
+  char text[] =
+      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+      "<kml xmlns=\"http://earth.google.com/kml/2.x\">"
+      "<Placemark>"
+      "<name>Simple placemark</name>"
+      "<description>Attached to the ground.</description>"
+      "<Point>"
+      "<coordinates>-122.0822035425683,37.42228990140251,0</coordinates>"
+      "</Point>"
+      "</Placemark>"
+      "<Placemark>"
+      "<name>Simple placemark</name>"
+      "<description>Attached to the ground.</description>"
+      "<Point>"
+      "<coordinates>-122.0822035425683,37.42228990140251,0\
+                    -122.0822035425683,37.42228990140251,0</coordinates>"
+      "</Point>"
+      "</Placemark>"
+      "<Placemark>"
+      "<name>Simple placemark</name>"
+      "<description>Attached to the ground.</description>"
+      "<Point>"
+      "<coordinates>-122.0822035425683,37.42228990140251,0</coordinates>"
+      "</Point>"
+      "</Placemark>"
+      "</kml>";
+
+  GpsFixArray locations;
+  std::string error;
+  EXPECT_TRUE(ParseKmlData(&locations, text, &error));
+  EXPECT_EQ("", error);
+  ASSERT_EQ(4U, locations.size());
+
+  for (unsigned i = 0; i < locations.size(); ++i) {
+    if (i != 2) {
+      EXPECT_EQ("Simple placemark", locations[i].name);
+      EXPECT_EQ("Attached to the ground.", locations[i].description);
+    } else {
+      EXPECT_EQ("", locations[i].name);
+      EXPECT_EQ("", locations[i].description);
+    }
+    EXPECT_FLOAT_EQ(-122.0822035425683, locations[i].longitude);
+    EXPECT_FLOAT_EQ(37.42228990140251, locations[i].latitude);
+    EXPECT_FLOAT_EQ(0, locations[i].elevation);
+  }
+}
+
+TEST(KmlParser, TraverseEmptyDoc) {
+  char text[] =
+      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+      "<kml xmlns=\"http://earth.google.com/kml/2.x\"></kml>";
+
+  GpsFixArray locations;
+  std::string error;
+  EXPECT_TRUE(ParseKmlData(&locations, text, &error));
+  EXPECT_EQ("", error);
+  EXPECT_EQ(0U, locations.size());
+}
+
+TEST(KmlParser, TraverseDocNoPlacemarks) {
+  char text[] =
+      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+      "<kml xmlns=\"http://earth.google.com/kml/2.x\">"
+      "<LineString></LineString>"
+      "<name></name>"
+      "</kml>";
+
+  GpsFixArray locations;
+  std::string error;
+  EXPECT_TRUE(ParseKmlData(&locations, text, &error));
+  EXPECT_EQ("", error);
+  EXPECT_EQ(0U, locations.size());
+}
+
+TEST(KmlParser, TraverseNestedDoc) {
+  char text[] =
+      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+      "<kml xmlns=\"http://earth.google.com/kml/2.x\">"
+      "<Document>"
+      "<Folder>"
+      "<name>Placemarks</name>"
+      "<description>These are just some of the different kinds of placemarks "
+      "with"
+      "which you can mark your favorite places</description>"
+      "<LookAt>"
+      "<longitude>-122.0839597145766</longitude>"
+      "<latitude>37.42222904525232</latitude>"
+      "<altitude>0</altitude>"
+      "<heading>-148.4122922628044</heading>"
+      "<tilt>40.5575073395506</tilt>"
+      "<range>500.6566641072245</range>"
+      "</LookAt>"
+      "<Placemark>"
+      "<name>Simple placemark</name>"
+      "<description>Attached to the ground.</description>"
+      "<Point>"
+      "<coordinates>-122.0822035425683,37.42228990140251,0</coordinates>"
+      "</Point>"
+      "</Placemark>"
+      "</Folder>"
+      "</Document>"
+      "</kml>";
+
+  GpsFixArray locations;
+  std::string error;
+  EXPECT_TRUE(ParseKmlData(&locations, text, &error));
+  EXPECT_EQ("", error);
+  ASSERT_EQ(1U, locations.size());
+
+  EXPECT_EQ("Simple placemark", locations[0].name);
+  EXPECT_EQ("Attached to the ground.", locations[0].description);
+  EXPECT_FLOAT_EQ(-122.0822035425683, locations[0].longitude);
+  EXPECT_FLOAT_EQ(37.42228990140251, locations[0].latitude);
+  EXPECT_FLOAT_EQ(0, locations[0].elevation);
+}
+
+TEST(KmlParser, ParsePlacemarkNullNameNoCrash) {
+  char text[] =
+      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+      "<kml xmlns=\"http://earth.google.com/kml/2.x\">"
+      "<Placemark>"
+      "<name/>"
+      "<description/>"
+      "<Point>"
+      "<coordinates>-122.0822035425683,37.42228990140251,0</coordinates>"
+      "</Point>"
+      "</Placemark>"
+      "</kml>";
+
+  GpsFixArray locations;
+  std::string error;
+  EXPECT_TRUE(ParseKmlData(&locations, text, &error));
+  ASSERT_EQ(1U, locations.size());
+  EXPECT_STREQ("", locations.front().name.c_str());
+  EXPECT_STREQ("", locations.front().description.c_str());
+}
+
+// Flaky test; uses locale.
+TEST(KmlParser, DISABLED_ParseLocationNormalCommaLocale) {
+  // auto scopedCommaLocale = setScopedCommaLocale();
+  char text[] =
+      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+      "<kml xmlns=\"http://earth.google.com/kml/2.x\">"
+      "<Placemark>"
+      "<name>Simple placemark</name>"
+      "<description>Attached to the ground.</description>"
+      "<Point>"
+      "<coordinates>-122.0822035425683,37.42228990140251,0</coordinates>"
+      "</Point>"
+      "</Placemark>"
+      "</kml>";
+
+  GpsFixArray locations;
+  std::string error;
+
+  ASSERT_TRUE(ParseKmlData(&locations, text, &error));
+
+  for (unsigned i = 0; i < locations.size(); ++i) {
+    EXPECT_EQ("Simple placemark", locations[i].name);
+    EXPECT_EQ("Attached to the ground.", locations[i].description);
+    EXPECT_FLOAT_EQ(-122.0822035425683, locations[i].longitude);
+    EXPECT_FLOAT_EQ(37.42228990140251, locations[i].latitude);
+    EXPECT_FLOAT_EQ(0, locations[i].elevation);
+  }
+}
+
+}  // namespace cuttlefish
\ No newline at end of file
diff --git a/host/commands/cvd_import_locations/unittest/main_test.cc b/host/commands/cvd_import_locations/unittest/main_test.cc
new file mode 100644
index 0000000..77cc9bc
--- /dev/null
+++ b/host/commands/cvd_import_locations/unittest/main_test.cc
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
\ No newline at end of file