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