blob: 1b69bed5b226bf9c70e15d1cc859231ef452eb3c [file] [log] [blame]
// Copyright 2011 Google Inc. All Rights Reserved.
//
// 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 "lexer.h"
#include "eval_env.h"
#include "test.h"
TEST(Lexer, ReadVarValue) {
Lexer lexer("plain text $var $VaR ${x}\nmore text");
StringPiece uneval;
std::string err;
EXPECT_TRUE(lexer.ReadBindingValue(&uneval, &err));
EXPECT_EQ("", err);
EXPECT_EQ("plain text $var $VaR ${x}\n", uneval.AsString());
std::string eval = EvaluateBindingForTesting(uneval);
EXPECT_EQ("plain text [$var] [$VaR] [$x]", eval);
}
TEST(Lexer, ReadEvalStringEscapes) {
Lexer lexer("$ $$ab c$: $\ncde\nmore text");
StringPiece uneval;
std::string err;
EXPECT_TRUE(lexer.ReadBindingValue(&uneval, &err));
EXPECT_EQ("", err);
EXPECT_EQ("$ $$ab c$: $\ncde\n", uneval.AsString());
std::string eval = EvaluateBindingForTesting(uneval);
EXPECT_EQ(" $ab c: cde", eval);
}
TEST(Lexer, ReadIdent) {
Lexer lexer("foo baR baz_123 foo-bar");
StringPiece ident;
EXPECT_TRUE(lexer.ReadIdent(&ident));
EXPECT_EQ("foo", ident);
EXPECT_TRUE(lexer.ReadIdent(&ident));
EXPECT_EQ("baR", ident);
EXPECT_TRUE(lexer.ReadIdent(&ident));
EXPECT_EQ("baz_123", ident);
EXPECT_TRUE(lexer.ReadIdent(&ident));
EXPECT_EQ("foo-bar", ident);
}
TEST(Lexer, ReadIdentCurlies) {
// Verify that ReadIdent includes dots in the name,
// but in an expansion $bar.dots stops at the dot.
Lexer lexer("foo.dots $bar.dots ${bar.dots}\n");
StringPiece ident;
EXPECT_TRUE(lexer.ReadIdent(&ident));
EXPECT_EQ("foo.dots", ident);
StringPiece uneval;
string err;
EXPECT_TRUE(lexer.ReadBindingValue(&uneval, &err));
EXPECT_EQ("", err);
EXPECT_EQ("$bar.dots ${bar.dots}\n", uneval.AsString());
std::string eval = EvaluateBindingForTesting(uneval);
EXPECT_EQ("[$bar].dots [$bar.dots]", eval);
}
TEST(Lexer, PeekCanonicalPath) {
auto peek_canon = [](const char* pattern) {
Lexer lexer(pattern);
return lexer.PeekCanonicalPath().AsString();
};
EXPECT_EQ("", peek_canon(""));
EXPECT_EQ("", peek_canon(" : "));
EXPECT_EQ("", peek_canon("/ : "));
EXPECT_EQ("", peek_canon("/foo/ : "));
EXPECT_EQ("", peek_canon("/foo/../bar : "));
EXPECT_EQ("", peek_canon("/../foo/bar : "));
EXPECT_EQ("", peek_canon("foo$$bar : "));
EXPECT_EQ("", peek_canon("foo${bar}baz : "));
EXPECT_EQ("/foo", peek_canon("/foo : "));
EXPECT_EQ("/foo/bar", peek_canon("/foo/bar : "));
EXPECT_EQ("foo", peek_canon("foo : "));
EXPECT_EQ("foo/bar", peek_canon("foo/bar : "));
EXPECT_EQ("../foo/bar", peek_canon("../foo/bar : "));
EXPECT_EQ("../../foo/bar", peek_canon("../../foo/bar : "));
EXPECT_EQ("foo", peek_canon("./foo : "));
EXPECT_EQ("../../foo/bar", peek_canon("./../../foo/bar : "));
}
TEST(Lexer, ReadPath) {
Lexer lexer("x$$y$ z$:: $bar.dots\n");
std::string err;
LexedPath uneval;
std::string eval;
EXPECT_TRUE(lexer.ReadPath(&uneval, &err));
EXPECT_EQ("", err);
EXPECT_EQ("x$$y$ z$:", uneval.str_.AsString());
eval = EvaluatePathForTesting(uneval);
EXPECT_EQ("x$y z:", eval);
EXPECT_EQ(Lexer::COLON, lexer.ReadToken());
EXPECT_TRUE(lexer.ReadPath(&uneval, &err));
EXPECT_EQ("", err);
EXPECT_EQ("$bar.dots", uneval.str_.AsString());
eval = EvaluatePathForTesting(uneval);
EXPECT_EQ("[$bar].dots", eval);
}
TEST(Lexer, Error) {
Lexer lexer("foo$\nbad $");
StringPiece uneval;
string err;
ASSERT_FALSE(lexer.ReadBindingValue(&uneval, &err));
EXPECT_EQ("input:2: bad $-escape (literal $ must be written as $$)\n"
"bad $\n"
" ^ near here"
, err);
}
TEST(Lexer, CommentEOF) {
// Verify we don't run off the end of the string when the EOF is
// mid-comment.
Lexer lexer("# foo");
Lexer::Token token = lexer.ReadToken();
EXPECT_EQ(Lexer::ERROR, token);
}
TEST(Lexer, Tabs) {
// Verify we print a useful error on a disallowed character.
Lexer lexer(" \tfoobar");
Lexer::Token token = lexer.ReadToken();
EXPECT_EQ(Lexer::INDENT, token);
token = lexer.ReadToken();
EXPECT_EQ(Lexer::ERROR, token);
EXPECT_EQ("tabs are not allowed, use spaces", lexer.DescribeLastError());
}
TEST(Lexer, AdvanceToNextManifestChunk) {
auto advance = [](std::string input) -> std::string {
// The input string may optionally have a '^' marking where the lexer should
// start scanning for a chunk.
size_t idx = input.find_first_of('^');
if (idx == std::string::npos) {
idx = 0;
} else {
input = input.substr(0, idx) + input.substr(idx + 1);
}
idx = AdvanceToNextManifestChunk(input, idx);
// The return string has a '^' marking where the manifest was split.
return input.substr(0, idx) + "^" + input.substr(idx);
};
EXPECT_EQ("^", advance(""));
EXPECT_EQ("A\n^B\n", advance("A\nB\n"));
EXPECT_EQ("\n^B\n", advance("\nB\n"));
EXPECT_EQ("\0\0\0^"_s, advance("\0\0\0"_s));
// An LF preceded by "$" or "$\r" might be part of a line continuator, so we
// skip it when looking for a place to split the manifest.
EXPECT_EQ("A$\nB\n^C", advance("^A$\nB\nC"));
EXPECT_EQ("A$\nB\n^C", advance("A^$\nB\nC"));
EXPECT_EQ("A$\nB\n^C", advance("A$^\nB\nC"));
EXPECT_EQ("A$\r\nB\n^C", advance("^A$\r\nB\nC"));
EXPECT_EQ("A$\r\nB\n^C", advance("A^$\r\nB\nC"));
EXPECT_EQ("A$\r\nB\n^C", advance("A$^\r\nB\nC"));
EXPECT_EQ("A$\r\nB\n^C", advance("A$\r^\nB\nC"));
// Skip over blank lines and comment lines.
EXPECT_EQ("\n^", advance("\n"));
EXPECT_EQ("\n\n^A\n", advance("\n\nA\n"));
EXPECT_EQ("\n \n^A", advance("\n \nA"));
EXPECT_EQ("\n#\n^A", advance("\n#\nA"));
}