blob: 5b6dcfb8a29fd9163512aac0236864f358647433 [file] [log] [blame]
/*
* Copyright 2017 WebAssembly Community Group participants
*
* 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 "gtest/gtest.h"
#include "source-maps.h"
// Use a macro instead of a function to get meaningful line numbers on failure.
#define EXPECT_SEGMENT_EQ(lhs, rhs) \
do { \
EXPECT_EQ(lhs.generated_col, rhs.generated_col); \
EXPECT_EQ(lhs.generated_col_delta, rhs.generated_col_delta); \
EXPECT_EQ(lhs.has_source, rhs.has_source); \
EXPECT_EQ(lhs.source, rhs.source); \
EXPECT_EQ(lhs.source_line, rhs.source_line); \
EXPECT_EQ(lhs.source_line_delta, rhs.source_line_delta); \
EXPECT_EQ(lhs.source_col, rhs.source_col); \
EXPECT_EQ(lhs.source_col_delta, rhs.source_col_delta); \
EXPECT_EQ(lhs.has_name, rhs.has_name); \
EXPECT_EQ(lhs.name, rhs.name); \
} while (0)
TEST(source_mappings, comparisons) {
SourceMapGenerator::SourceMapping a = {{1, 1}, {1, 1}, 0};
SourceMapGenerator::SourceMapping b = {{1, 1}, {1, 1}, 0};
EXPECT_TRUE(a == b);
EXPECT_FALSE(a < b);
EXPECT_FALSE(b < a);
b = {{1, 1}, {2, 1}, 0};
EXPECT_FALSE(a == b);
EXPECT_TRUE(a < b);
EXPECT_FALSE(b < a);
b = {{1, 1}, {1, 2}, 0};
EXPECT_FALSE(a == b);
EXPECT_TRUE(a < b);
EXPECT_FALSE(b < a);
b = {{1, 0}, {1, 2}, 0};
EXPECT_FALSE(a == b);
EXPECT_TRUE(a < b);
EXPECT_FALSE(b < a);
b = {{0, 0}, {1, 2}, 0};
EXPECT_FALSE(a == b);
EXPECT_TRUE(a < b);
EXPECT_FALSE(b < a);
b = {{1, 2}, {1, 0}, 0};
EXPECT_FALSE(a == b);
EXPECT_FALSE(a < b);
EXPECT_TRUE(b < a);
b = {{1, 1}, {1, 1}, 1};
EXPECT_FALSE(a == b);
EXPECT_TRUE(a < b);
EXPECT_FALSE(b < a);
}
TEST(source_maps, constructor) { SourceMapGenerator("file", "source-root"); }
TEST(source_maps, sources) {
SourceMapGenerator smg("source.out", "source-root");
smg.AddMapping({2, 3}, {1, 1}, "asdf1");
smg.AddMapping({2, 2}, {1, 1}, "");
smg.AddMapping({2, 3}, {1, 1}, "asdf2");
const auto& map = smg.GetMap();
EXPECT_EQ("source.out", map.file);
EXPECT_EQ("source-root", map.source_root);
ASSERT_EQ(3UL, map.sources.size());
EXPECT_EQ("asdf1", map.sources[0]);
EXPECT_EQ("", map.sources[1]);
EXPECT_EQ("asdf2", map.sources[2]);
SourceMapGenerator smg2("", "");
const auto& map2 = smg2.GetMap();
EXPECT_EQ("", map2.file);
EXPECT_EQ("", map2.source_root);
EXPECT_EQ(0UL, map2.sources.size());
}
TEST(source_maps, zero_mappings) {
SourceMapGenerator smg("", "");
smg.AddMapping({1, 0}, {1, 0}, "");
const auto& map = smg.GetMap();
ASSERT_EQ(1UL, map.segment_groups.size());
EXPECT_EQ(1U, map.segment_groups.back().generated_line);
ASSERT_EQ(1UL, map.segment_groups.back().segments.size());
const auto& seg = map.segment_groups.back().segments.back();
SourceMap::Segment s = {{0, 0}, {true, 0}, {1, 1}, {0, 0}, {false, 0}};
EXPECT_SEGMENT_EQ(s, seg);
}
TEST(source_maps, invalid_mappings) {
SourceMapGenerator smg("", "");
// For now gen, orig, and source are all required.
EXPECT_FALSE(smg.AddMapping({0, 1}, {1, 1}, ""));
EXPECT_FALSE(smg.AddMapping({1, 1}, {0, 1}, ""));
EXPECT_TRUE(smg.AddMapping({1, 0}, {1, 1}, ""));
EXPECT_TRUE(smg.AddMapping({1, 1}, {1, 0}, ""));
}
TEST(source_maps, incremental_mappings) {
// Check cases where there is no delta; i.e. the first instances of fields.
SourceMapGenerator smg("", "");
smg.AddMapping({4, 7}, {5, 1}, "asdf");
auto& map = smg.GetMap();
ASSERT_TRUE(map.Validate(true));
ASSERT_EQ(4UL, map.segment_groups.size());
EXPECT_EQ(4U, map.segment_groups.back().generated_line);
ASSERT_EQ(1UL, map.segment_groups.back().segments.size());
const auto& seg = map.segment_groups.back().segments.back();
SourceMap::Segment s = {{7, 7}, {true, 0}, {5, 5}, {1, 1}, {false, 0}};
EXPECT_SEGMENT_EQ(s, seg);
// Duplicate mapping (no new segment)
smg.AddMapping({4, 7}, {5, 1}, "asdf");
smg.GetMap();
ASSERT_TRUE(map.Validate(true));
ASSERT_EQ(4UL, map.segment_groups.size());
EXPECT_EQ(4U, map.segment_groups.back().generated_line);
ASSERT_EQ(1UL, map.segment_groups.back().segments.size());
// New generated column, same line, same source
smg.AddMapping({4, 8}, {5, 1}, "asdf");
smg.GetMap();
ASSERT_TRUE(map.Validate(true));
ASSERT_EQ(4UL, map.segment_groups.size());
EXPECT_EQ(4U, map.segment_groups.back().generated_line);
ASSERT_EQ(2UL, map.segment_groups.back().segments.size());
// s = {{8, 1}, {true, 0}, {5, 1}, {1, 0}, {false, 0}};
// Not sure which is more readable; pass a whole new segment on one line or
// update by field name?
s.generated_col = 8;
s.generated_col_delta = 1;
s.source_line_delta = 0;
s.source_col_delta = 0;
EXPECT_SEGMENT_EQ(s, map.segment_groups.back().segments.back());
// New generated column, same line, new source col
smg.AddMapping({4, 9}, {5, 2}, "asdf");
smg.GetMap();
ASSERT_TRUE(map.Validate(true));
ASSERT_EQ(4UL, map.segment_groups.size());
EXPECT_EQ(4U, map.segment_groups.back().generated_line);
ASSERT_EQ(3UL, map.segment_groups.back().segments.size());
s = {{9, 1}, {true, 0}, {5, 0}, {2, 1}, {false, 0}};
EXPECT_SEGMENT_EQ(s, map.segment_groups.back().segments.back());
// New generated column, same line, new source line, negative source col delta
smg.AddMapping({4, 10}, {6, 0}, "asdf");
smg.GetMap();
ASSERT_TRUE(map.Validate(true));
ASSERT_EQ(4UL, map.segment_groups.size());
EXPECT_EQ(4U, map.segment_groups.back().generated_line);
ASSERT_EQ(4UL, map.segment_groups.back().segments.size());
s = {{10, 1}, {true, 0}, {6, 1}, {0, -2}, {false, 0}};
EXPECT_SEGMENT_EQ(s, map.segment_groups.back().segments.back());
// Same generated line and col, different source.
// The JS sourcemapper allows and encodes this
// (I guess it overrides the previous mapping?)
smg.AddMapping({4, 10}, {7, 10}, "asdf");
smg.GetMap();
ASSERT_TRUE(map.Validate(true));
ASSERT_EQ(4UL, map.segment_groups.size());
EXPECT_EQ(4U, map.segment_groups.back().generated_line);
ASSERT_EQ(5UL, map.segment_groups.back().segments.size());
s = {{10, 0}, {true, 0}, {7, 1}, {10, 10}, {false, 0}};
EXPECT_SEGMENT_EQ(s, map.segment_groups.back().segments.back());
// New generated col, negative source col delta
smg.AddMapping({4, 11}, {7, 9}, "asdf");
smg.GetMap();
ASSERT_TRUE(map.Validate(true));
ASSERT_EQ(4UL, map.segment_groups.size());
EXPECT_EQ(4U, map.segment_groups.back().generated_line);
ASSERT_EQ(6UL, map.segment_groups.back().segments.size());
s = {{11, 1}, {true, 0}, {7, 0}, {9, -1}, {false, 0}};
EXPECT_SEGMENT_EQ(s, map.segment_groups.back().segments.back());
// New generated line (new segment, leave 1 hole)
smg.AddMapping({6, 1}, {8, 0}, "asdf");
smg.GetMap();
ASSERT_TRUE(map.Validate(true));
ASSERT_EQ(6UL, map.segment_groups.size());
// Empty segment at line 5
EXPECT_EQ(5U, map.segment_groups[4].generated_line);
ASSERT_EQ(0UL, map.segment_groups[4].segments.size());
// Populated segment at line 6
EXPECT_EQ(6U, map.segment_groups.back().generated_line);
ASSERT_EQ(1UL, map.segment_groups.back().segments.size());
// Generated col delta is 1 because it's a new line
s = {{1, 1}, {true, 0}, {8, 1}, {0, -9}, {false, 0}};
EXPECT_SEGMENT_EQ(s, map.segment_groups.back().segments.back());
// New generated line inserted into the hole
smg.AddMapping({5, 1}, {8, 0}, "asdf");
smg.GetMap();
ASSERT_TRUE(map.Validate(true));
ASSERT_EQ(6UL, map.segment_groups.size());
EXPECT_EQ(5U, map.segment_groups[4].generated_line);
ASSERT_EQ(1UL, map.segment_groups[4].segments.size());
// Inserted segment. Generated col delta is 0
s = {{1, 1}, {true, 0}, {8, 1}, {0, -9}, {false, 0}};
EXPECT_SEGMENT_EQ(s, map.segment_groups[4].segments.back());
// Following segment
s = {{1, 1}, {true, 0}, {8, 0}, {0, 0}, {false, 0}};
EXPECT_SEGMENT_EQ(s, map.segment_groups.back().segments.back());
}
#define EXPECT_JSON_CONTAINS_STR(output, key, value) \
EXPECT_TRUE(output.find(std::string("\"") + key + "\" : \"" + value + "\"") != std::string::npos)
#define EXPECT_JSON_CONTAINS_NUM(output, key, value) \
EXPECT_TRUE(output.find(std::string("\"") + key + "\" : " + value) != std::string::npos)
TEST(source_maps, serialization_empty) {
SourceMapGenerator smg("source.out", "source-root");
std::string output = smg.SerializeMappings();
EXPECT_JSON_CONTAINS_NUM(output, "version", "3");
EXPECT_JSON_CONTAINS_STR(output, "file", "source.out");
EXPECT_JSON_CONTAINS_STR(output, "sourceRoot", "source-root");
EXPECT_JSON_CONTAINS_NUM(output, "sources", "[]"); // TODO: fix this abuse of NUM
}