Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the integration test suite for the readString API #43

Merged
merged 4 commits into from
Mar 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ballerina/Ballerina.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
org = "ballerina"
name = "yaml"
version = "0.5.2"
version = "0.5.3"
authors = ["Ballerina"]
keywords = ["yaml"]
repository = "https://github.com/ballerina-platform/module-ballerina-yaml"
Expand Down
2 changes: 1 addition & 1 deletion ballerina/Dependencies.toml
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ dependencies = [
[[package]]
org = "ballerina"
name = "yaml"
version = "0.5.2"
version = "0.5.3"
dependencies = [
{org = "ballerina", name = "file"},
{org = "ballerina", name = "io"},
Expand Down
4 changes: 2 additions & 2 deletions ballerina/modules/composer/state.bal
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ public class ComposerState {
# Flag is set if same map keys are allowed in a mapping
readonly & boolean allowMapEntryRedefinition;

public isolated function init(string[]|string yamlInput, map<schema:YAMLTypeConstructor> tagSchema,
public isolated function init(string[] lines, map<schema:YAMLTypeConstructor> tagSchema,
boolean allowAnchorRedefinition, boolean allowMapEntryRedefinition) returns parser:ParsingError? {

self.parserState = check new (yamlInput);
self.parserState = check new (lines);
self.tagSchema = tagSchema;
self.allowAnchorRedefinition = allowAnchorRedefinition;
self.allowMapEntryRedefinition = allowMapEntryRedefinition;
Expand Down
6 changes: 0 additions & 6 deletions ballerina/modules/lexer/lexer.bal
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,6 @@ public isolated function scan(LexerState state) returns LexerState|LexicalError
return state.index == 0 ? state.tokenize(EMPTY_LINE) : state.tokenize(EOL);
}

// Check for line breaks when reading from string
if state.peek() == "\n" && state.context != LEXER_DOUBLE_QUOTE {
state.isNewLine = true;
return state.tokenize(EOL);
}

match state.context {
LEXER_START => {
return contextStart(state);
Expand Down
2 changes: 1 addition & 1 deletion ballerina/modules/lexer/scan.bal
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ isolated function scanPlanarChar(LexerState state) returns boolean|LexicalError
}

// Step back from the white spaces if EOL or ':' is reached
if state.peek() == () || state.peek() == "\n" {
if state.peek() == () || state.isNewLine() {
state.forward(-numWhitespace);
return true;
}
Expand Down
11 changes: 6 additions & 5 deletions ballerina/modules/lexer/state.bal
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,6 @@ public class LexerState {

public boolean firstLine = true;

public boolean isNewLine = false;

int mappingKeyColumn = -1;

# Output YAML token
Expand Down Expand Up @@ -150,7 +148,6 @@ public class LexerState {
self.indentStartIndex = -1;
self.tokensForMappingValue = [];
self.tabInWhitespace = -1;
self.isNewLine = false;
self.keyDefinedForLine = false;
}

Expand All @@ -167,6 +164,10 @@ public class LexerState {
}

public isolated function isFlowCollection() returns boolean => self.numOpenedFlowCollections > 0;

public isolated function isEndOfStream() returns boolean => self.index >= self.line.length();

# Check if the current character is a new line.
# This should be replaced by the os module once it supports an API: #4931.
#
# + return - True if the current character is a new line
public isolated function isNewLine() returns boolean => self.peek() == "\n" || self.peek() == "\r\n";
}
4 changes: 2 additions & 2 deletions ballerina/modules/parser/parser.bal
Original file line number Diff line number Diff line change
Expand Up @@ -250,8 +250,8 @@ public isolated function parse(ParserState state, ParserOption option = DEFAULT,
# + return - True if the string is a valid planar scalar. Else, false.
public isolated function isValidPlanarScalar(string value) returns boolean {
string? planarScalarResult = ();
do {
ParserState parserState = check new (value);
do {
ParserState parserState = check new ([value]);
planarScalarResult = check planarScalar(parserState, false);
} on fail {
return false;
Expand Down
40 changes: 8 additions & 32 deletions ballerina/modules/parser/state.bal
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,12 @@
// 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.

import yaml.lexer;
import yaml.common;

public class ParserState {
# Properties for the YAML lines
string[]|string yamlInput;
string[] lines;
int numLines;
int lineIndex = -1;

Expand Down Expand Up @@ -62,12 +61,9 @@ public class ParserState {

common:Event[] eventBuffer = [];

boolean isStringInput;

public isolated function init(string[]|string yamlInput) returns ParsingError? {
self.yamlInput = yamlInput;
self.isStringInput = yamlInput is string;
self.numLines = self.isStringInput ? 1 : (<string[]>yamlInput).length();
public isolated function init(string[] lines) returns ParsingError? {
self.lines = lines;
self.numLines = lines.length();
ParsingError? err = self.initLexer();
if err is ParsingError {
self.eventBuffer.push({endType: common:STREAM});
Expand All @@ -90,36 +86,16 @@ public class ParserState {
self.lineIndex += 1;
string line;

if self.isStringInput {
string currentLine = self.lexerState.line;
if self.lexerState.isNewLine {
line = currentLine.substring(self.lexerState.index);
} else {
if self.lineIndex == 0 {
line = <string>self.yamlInput;
} else {
int? index = currentLine.indexOf("\n");
if index is int {
line = currentLine.substring(index + 1);
} else {
return generateGrammarError(self, message);
}
}
}
} else {
if self.lineIndex >= self.numLines {
return generateGrammarError(self, message);
}
string[] lines = <string[]>self.yamlInput;
line = lines[self.lineIndex];
if self.lineIndex >= self.numLines {
return generateGrammarError(self, message);
}
line = self.lines[self.lineIndex];

self.explicitDoc = false;
self.expectBlockSequenceValue = false;
self.tagPropertiesInLine = false;
self.lexerState.setLine(line, self.lineIndex);
}

isolated function isEndOfFile() returns boolean =>
self.isStringInput ? self.lexerState.isEndOfStream() : self.lineIndex >= self.numLines - 1;
isolated function isEndOfFile() returns boolean => self.lineIndex >= self.numLines - 1;
}
6 changes: 2 additions & 4 deletions ballerina/tests/api_test.bal
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ import ballerina/io;
import ballerina/test;

@test:Config {
groups: ["api"],
enable: false
groups: ["api"]
}
function testReadYamlString() returns error? {
string input = string `
Expand Down Expand Up @@ -76,8 +75,7 @@ function testWriteYamlFile() returns error? {

@test:Config {
dataProvider: yamlSchemaDataGen,
groups: ["api"],
enable: false
groups: ["api"]
}
function testReadYAMLSchema(YAMLSchema schema, json expectedOutput) returns error? {
string input = string `
Expand Down
25 changes: 21 additions & 4 deletions ballerina/tests/it.bal
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import ballerina/io;
import ballerina/test;

YamlType[] customYamlTypes = [];
Expand All @@ -35,7 +36,7 @@ map<FailSafeSchema> customTags = {
};

@test:BeforeSuite
function initYamlCustomeTypes() {
function initYamlCustomTypes() {
customTags.entries().forEach(function([string, FailSafeSchema] entry) {
customYamlTypes.push({
tag: entry[0],
Expand All @@ -50,11 +51,27 @@ function initYamlCustomeTypes() {
@test:Config {
dataProvider: yamlDataGen
}
function testYAMLIntegrationTest(string filePath, json expectedOutput, boolean isStream, boolean isError) returns error? {
function testYAMLIntegrationTestForReadFile(string filePath, json expectedOutput, boolean isStream, boolean isError) returns error? {
json|Error output = readFile(filePath, yamlTypes = customYamlTypes, isStream = isStream);
assertOutput(output, expectedOutput, isError);
}

@test:Config {
dataProvider: yamlDataGen
}
function testYAMLIntegrationTestForReadString(string filePath, json expectedOutput, boolean isStream, boolean isError) returns error? {
io:ReadableByteChannel byteChannel = check io:openReadableFile(filePath);
(byte[] & readonly) readBytes = check byteChannel.readAll();
string yamlContent = check string:fromBytes(readBytes);

json|Error output = readString(yamlContent, yamlTypes = customYamlTypes, isStream = isStream);
assertOutput(output, expectedOutput, isError);
}

function assertOutput(json|Error actualOut, json expectedOut, boolean isError) {
if isError {
test:assertTrue(output is Error);
test:assertTrue(actualOut is Error);
} else {
test:assertEquals(output, expectedOutput);
test:assertEquals(actualOut, expectedOut);
}
}
15 changes: 14 additions & 1 deletion ballerina/utils.bal
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@
// 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.

import yaml.schema;
import yaml.composer;

import ballerina/file;

# Checks if the file exists. If not, creates a new file.
Expand Down Expand Up @@ -61,3 +62,15 @@ isolated function generateTagHandlesMap(YamlType[] yamlTypes, YAMLSchema yamlSch

return tagHandles;
}

# Parses the given lines of YAML and returns the JSON representation.
#
# + lines - Lines of the YAML document
# + config - Configuration for the YAML parser
# + return - JSON representation of the YAML document
isolated function readLines(string[] lines, ReadConfig config) returns json|Error {
composer:ComposerState composerState = check new (lines, generateTagHandlesMap(config.yamlTypes, config.schema),
config.allowAnchorRedefinition, config.allowMapEntryRedefinition
);
return config.isStream ? composer:composeStream(composerState) : composer:composeDocument(composerState);
}
13 changes: 5 additions & 8 deletions ballerina/yaml.bal
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,17 @@
import ballerina/io;
import yaml.emitter;
import yaml.serializer;
import yaml.composer;

# Parses a Ballerina string of YAML content into a Ballerina map object.
#
# + yamlString - YAML content
# + config - Configuration for reading a YAML file
# + return - YAML map object on success. Else, returns an error
public isolated function readString(string yamlString, *ReadConfig config) returns json|Error {
composer:ComposerState composerState = check new (yamlString,
generateTagHandlesMap(config.yamlTypes, config.schema), config.allowAnchorRedefinition,
config.allowMapEntryRedefinition);
return composer:composeDocument(composerState);
io:ReadableByteChannel byteChannel = check io:createReadableChannel(yamlString.toBytes());
io:ReadableCharacterChannel charChannel = new (byteChannel, io:DEFAULT_ENCODING);
string[] lines = check charChannel.readAllLines();
return readLines(lines, config);
}

# Parses a YAML file into a Ballerina json object.
Expand All @@ -36,9 +35,7 @@ public isolated function readString(string yamlString, *ReadConfig config) retur
# + return - YAML map object on success. Else, returns an error
public isolated function readFile(string filePath, *ReadConfig config) returns json|Error {
string[] lines = check io:fileReadLines(filePath);
composer:ComposerState composerState = check new (lines, generateTagHandlesMap(config.yamlTypes, config.schema),
config.allowAnchorRedefinition, config.allowMapEntryRedefinition);
return config.isStream ? composer:composeStream(composerState) : composer:composeDocument(composerState);
return readLines(lines, config);
}

# Converts the YAML structure to an array of strings.
Expand Down
Loading