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

feat: test file generator #11

Merged
merged 7 commits into from
Dec 2, 2023
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
24 changes: 18 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,14 @@ After that, get your session token from the cookie:
Add the value to the `session` variable in the `day_generator.dart` file . This will allow the script to populate your input file.


### Naming conventions

When using the Boilerplate generator, everything is done for you automatically. However, if you create a solution or input file by yourself: make sure it has a 2-digit number. Concretely, pad days 1-9 as `Day01.dart` for solutions and `aoc01.txt` for input.


### Boilerplate Generation

In the root of your directory, run
```dart
dart run day_generator.dart <day>
```

This will create an input file and a solution file with all the needed boilerplate to have a quick start. It also adds the solution to the corresponding index file, so the solution get imported into `main` automatically.
This will create an input and test file and a solution file with all the needed boilerplate to have a quick start. It also adds the solution to the corresponding index file, so the solution get imported into `main` automatically.

### Main

Expand All @@ -51,6 +46,23 @@ in the root of your directory.
By default the main file will only show the last solution. If you want to see all of them, you can use the `-a` or `--all` flag.
You can list all the command line arguments by using the `-h` or `--help` flag.

### Tests

A test file is automatically generated for each day. It contains tests for both parts of the example and the real input.

All you have to do is **fill out the variables given at the top of the test file.**

---

## Class Documentation

Below you can find a short documentation of the classes and methods provided by this starter project.

### Naming conventions

When using the Boilerplate generator, everything is done for you automatically. However, if you create a solution or input file by yourself: make sure it has a 2-digit number. Concretely, pad days 1-9 as `Day01.dart` for solutions and `aoc01.txt` for input.


### Generic Day

The abstract class all individual days subclass from. When constructed with the correct `day`, it automatically ready the corresponding input file and provides it with the `InputUtil`. To access it, just call `input` inside your class.
Expand Down
97 changes: 95 additions & 2 deletions day_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,17 @@ void main(List<String?> args) async {
// Create lib file
final dayFileName = 'day$dayNumber.dart';
unawaited(
File('solutions/$dayFileName').writeAsString(dayTemplate(dayNumber)),
File('solutions/$dayFileName')
.create(recursive: true)
.then((file) => file.writeAsString(_dayTemplate(dayNumber!))),
);

// Create test file
final testFileName = 'day${dayNumber}_test.dart';
unawaited(
File('test/$testFileName')
.create(recursive: true)
.then((file) => file.writeAsString(_testTemplate(dayNumber!))),
);

final exportFile = File('solutions/index.dart');
Expand Down Expand Up @@ -78,7 +88,7 @@ void main(List<String?> args) async {
print('All set, Good luck!');
}

String dayTemplate(String dayNumber) {
String _dayTemplate(String dayNumber) {
return '''
import '../utils/index.dart';

Expand All @@ -105,3 +115,86 @@ class Day$dayNumber extends GenericDay {

''';
}

String _testTemplate(String day) {
return '''
import 'package:test/test.dart';

import '../solutions/day$day.dart';

// *******************************************************************
// Fill out the variables below according to the puzzle description!
// The test code should usually not need to be changed, apart from uncommenting
// the puzzle tests for regression testing.
// *******************************************************************

/// Paste in the small example that is given for the FIRST PART of the puzzle.
/// It will be evaluated again the `_exampleSolutionPart1` below.
/// Make sure to respect the multiline string format to avoid additional
/// newlines at the end.
const _exampleInput1 = \'''
\''';

/// Paste in the small example that is given for the SECOND PART of the puzzle.
/// It will be evaluated against the `_exampleSolutionPart2` below.
///
/// In case the second part uses the same example, uncomment below line instead:
// const _exampleInput2 = _exampleInput;
const _exampleInput2 = \'''
\''';

/// The solution for the FIRST PART's example, which is given by the puzzle.
const _exampleSolutionPart1 = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a different value than 0, Like -1 or null? The tests would pass by default since the default return value is 0 as well. Not sure if this is the desired behaviour

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would indeed pass at the start, which might not be great from a purist POV.
But as this will not pass as soon as the user adds the actual solution from the example (or adds his solution) - and I expect them to do this when they use the test - I think it is ok.
An even bigger case for me to leave it as is: The examplePart2 will not be distracting/annoying (red 😅) until you acutally start to work at it :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point 👍


/// The solution for the SECOND PART's example, which is given by the puzzle.
const _exampleSolutionPart2 = 0;

/// The actual solution for the FIRST PART of the puzzle, based on your input.
/// This can only be filled out after you have solved the puzzle and is used
/// for regression testing when refactoring.
/// As long as the variable is `null`, the tests will be skipped.
const _puzzleSolutionPart1 = null;

/// The actual solution for the SECOND PART of the puzzle, based on your input.
/// This can only be filled out after you have solved the puzzle and is used
/// for regression testing when refactoring.
/// As long as the variable is `null`, the tests will be skipped.
const _puzzleSolutionPart2 = null;

void main() {
group(
'Day $day - Example Input',
() {
test('Part 1', () {
final day = Day$day()..inputForTesting = _exampleInput1;
expect(day.solvePart1(), _exampleSolutionPart1);
});
test('Part 2', () {
final day = Day$day()..inputForTesting = _exampleInput2;
expect(day.solvePart2(), _exampleSolutionPart2);
});
},
);
group(
'Day $day - Puzzle Input',
() {
final day = Day$day();
test(
'Part 1',
skip: _puzzleSolutionPart1 == null
? 'Skipped because _puzzleSolutionPart1 is null'
: false,
() => expect(day.solvePart1(), _puzzleSolutionPart1),
);
test(
'Part 2',
skip: _puzzleSolutionPart2 == null
? 'Skipped because _puzzleSolutionPart2 is null'
: false,
() => expect(day.solvePart1(), _puzzleSolutionPart2),
);
},
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we keep the Puzzle Input group commented out by default to prevent the tests from failing?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Else the test suite would still fail until you provide the _puzzleSolutionPart1 and _puzzleSolutionPart2 variables and make it less clear that your solution worked.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point! Pushed the changes

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could be a bit more "smart" about it and skip the test if the solution hasn't been filled in:

const _puzzleSolutionPart1 = null;
const _puzzleSolutionPart2 = null;

group(
    'Day 01 - Puzzle Input',
    () {
      final day = Day01();
      test(
        'Part 1',
        skip: _puzzleSolutionPart1 == null
            ? 'Skipped because _puzzleSolutionPart1 is null'
            : false,
        () => expect(day.solvePart1(), _puzzleSolutionPart1),
      );
      test(
        'Part 1',
        skip: _puzzleSolutionPart2 == null
            ? 'Skipped because _puzzleSolutionPart2 is null'
            : false,
        () => expect(day.solvePart1(), _puzzleSolutionPart2),
      );
    },
  );

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is perfect! I will add and commit it! 🚀
Was also thinking about skipping the tests without touching the actual test code, but stopped at the thought of a shouldTest boolean set by the user 😅
Thanks a lot! 💙

}
''';
}
Loading