blob: d39c5962ad0ac039cfd47ca0e64eb6b1388b5414 [file] [log] [blame]
// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "foomatic_shell/grammar.h"
#include "foomatic_shell/scanner.h"
#include <gtest/gtest.h>
#include <string>
#include <vector>
namespace foomatic_shell {
namespace {
// This function takes the input script as |input| and a sequence of tokens
// produced by a scanner (|tokens|). It generates and returns the string
// representation of the generated tokens. The returned string has the same
// length as |input|. The corresponding characters are set depending on the
// type of token covering given range according to the following rules:
// - 'B' - Byte
// - 'E' - ExecutedString
// - 'I' - InterpretedString
// - 'L' - LiteralString
// - 'S' - Space
// Positions that do not belong to any token are set to spaces.
// Example:
// - input string : abcde'rft' "dsfds"; `aaa` | bbb
// - representation: NNNNN LLL SS IIIII BS EEE SBSNNN
//
// The returned representation is calculated from |tokens|, not from |input|.
// The |input| string must be a reference to the same string as given to the
// scanner that produced |tokens|.
std::string CreateTokensRepresentation(const std::string& input,
const std::vector<Token>& tokens) {
std::string out(input.size(), ' ');
for (const Token& token : tokens) {
char c = 'x';
switch (token.type) {
case Token::Type::kByte:
c = 'B';
break;
case Token::Type::kExecutedString:
c = 'E';
break;
case Token::Type::kInterpretedString:
c = 'I';
break;
case Token::Type::kLiteralString:
c = 'L';
break;
case Token::Type::kNativeString:
c = 'N';
break;
case Token::Type::kSpace:
c = 'S';
break;
default:
break;
}
const size_t begin = token.begin - input.begin();
const size_t end = token.end - input.begin();
for (size_t i = begin; i < end; ++i)
out[i] = c;
}
return out;
}
TEST(Scanner, StringTypes) {
const std::string input = "command 'lit str' `exe str` \"int str\" nat str";
const std::string types = "NNNNNNNS LLLLLLL S EEEEEEE SS IIIIIII SNNNSNNN";
Scanner scanner(input);
std::vector<Token> tokens;
EXPECT_TRUE(scanner.ParseWholeInput(&tokens));
EXPECT_EQ(types, CreateTokensRepresentation(input, tokens));
}
TEST(Scanner, ExecutedStringInsideInterpretedString) {
const std::string input = "command \"int str1`exe str`int str2\" ";
const std::string types = "NNNNNNNS IIIIIIII EEEEEEE IIIIIIII SS";
Scanner scanner(input);
std::vector<Token> tokens;
EXPECT_TRUE(scanner.ParseWholeInput(&tokens));
EXPECT_EQ(types, CreateTokensRepresentation(input, tokens));
}
TEST(Scanner, UnterminatedLiteralString) {
const std::string input = "command 'int str1`exe str`int s";
Scanner scanner(input);
std::vector<Token> tokens;
EXPECT_FALSE(scanner.ParseWholeInput(&tokens));
EXPECT_EQ(scanner.GetPosition(), input.end());
}
TEST(Scanner, UnterminatedInterpretedString) {
const std::string input = "command \"int str1`exe str`int s";
Scanner scanner(input);
std::vector<Token> tokens;
EXPECT_FALSE(scanner.ParseWholeInput(&tokens));
EXPECT_EQ(scanner.GetPosition(), input.end());
}
TEST(Scanner, UnterminatedExecutedString) {
const std::string input = "command 'int str1' `exe str";
Scanner scanner(input);
std::vector<Token> tokens;
EXPECT_FALSE(scanner.ParseWholeInput(&tokens));
EXPECT_EQ(scanner.GetPosition(), input.end());
}
TEST(Scanner, CommandWithParameters) {
const std::string input =
"pdftops '9195' 'root' 'split_streams.pdf' '1' "
"' finishings=3 number-up=1 document=split.pdf' '/cups/tmp/foo-B65TL1'";
const std::string types =
"NNNNNNNS LLLL S LLLL S LLLLLLLLLLLLLLLLL S L S"
" LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL S LLLLLLLLLLLLLLLLLLLL ";
std::vector<Token> tokens;
Scanner scanner(input);
EXPECT_TRUE(scanner.ParseWholeInput(&tokens));
EXPECT_EQ(types, CreateTokensRepresentation(input, tokens));
}
TEST(Scanner, Pipeline) {
const std::string input = "ls -h | grep 'XXX' | wc -l; echo \"Done\"; ";
const std::string types = "NNSNNSBSNNNNS LLL SBSNNSNNBSNNNNS IIII BS";
std::vector<Token> tokens;
Scanner scanner(input);
EXPECT_TRUE(scanner.ParseWholeInput(&tokens));
EXPECT_EQ(types, CreateTokensRepresentation(input, tokens));
}
TEST(Scanner, Subshell) {
const std::string input =
"VAR1='xx' VAR=acs'zzz'qq my_app -par1 par2'qqq'"
" ; (echo ttt | tr t T; echo Done) | cat myfile.txt";
const std::string types =
"NNNNB LL SNNNBNNN LLL NNSSNNNNNNSSNNNNNSNNNN LLL "
"SBSBNNNNSNNNSBSNNSNSNBSNNNNSNNNNBSBSNNNSNNNNNNNNNN";
std::vector<Token> tokens;
Scanner scanner(input);
EXPECT_TRUE(scanner.ParseWholeInput(&tokens));
EXPECT_EQ(types, CreateTokensRepresentation(input, tokens));
}
} // namespace
} // namespace foomatic_shell