From c12f106fcdc8232cd83c55baef95a8a9d7f39568 Mon Sep 17 00:00:00 2001 From: "kai [they]" Date: Sun, 15 Oct 2023 16:44:32 -0700 Subject: [PATCH] Add snippet generation (#84) --- snippets/go/sort_bubble_sort_test.go | 50 ++++++++ snippets/go/sort_builtin_test.go | 5 + snippets/go/sort_insertion_sort_test.go | 34 +++++ snippets/go/sort_merge_sort_test.go | 69 ++++++++++ snippets/go/sort_selection_sort_test.go | 37 ++++++ snippets/js/sort_bubble_sort.js | 35 ++++++ snippets/js/sort_builtin.js | 5 + snippets/js/sort_insertion_sort.js | 41 ++++++ snippets/js/sort_selection_sort.js | 48 +++++++ snippets/python/sort_bubble_sort.py | 54 ++++++++ snippets/python/sort_builtin.py | 4 + snippets/python/sort_insertion_sort.py | 37 ++++++ snippets/python/sort_selection_sort.py | 47 +++++++ snippets/ruby/sort_bubble_sort.rb | 35 ++++++ snippets/ruby/sort_builtin.rb | 5 + snippets/ruby/sort_insertion_sort.rb | 46 +++++++ snippets/ruby/sort_selection_sort.rb | 47 +++++++ snippets/rust/sort_bubble_sort.rs | 37 ++++++ snippets/rust/sort_builtin.rs | 5 + snippets/rust/sort_insertion_sort.rs | 28 +++++ snippets/rust/sort_selection_sort.rs | 37 ++++++ src/go/sort_bubble_sort_test.go | 12 +- src/go/sort_builtin_test.go | 12 +- src/go/sort_insertion_sort_test.go | 12 +- src/go/sort_merge_sort_test.go | 12 +- src/go/sort_selection_sort_test.go | 12 +- src/js/sort_bubble_sort.js | 22 ++-- src/js/sort_builtin.js | 16 +-- src/js/sort_insertion_sort.js | 41 +++--- src/js/sort_selection_sort.js | 16 +-- src/python/sort_bubble_sort.py | 14 +-- src/python/sort_builtin.py | 14 +-- src/python/sort_insertion_sort.py | 14 +-- src/python/sort_selection_sort.py | 14 +-- src/ruby/sort_bubble_sort.rb | 12 +- src/ruby/sort_builtin.rb | 12 +- src/ruby/sort_insertion_sort.rb | 12 +- src/ruby/sort_selection_sort.rb | 12 +- src/rust/sort_bubble_sort.rs | 12 +- src/rust/sort_builtin.rs | 12 +- src/rust/sort_insertion_sort.rs | 68 +++++----- src/rust/sort_selection_sort.rs | 86 ++++++------- tasks.py | 160 ++++++++++++++++++++---- 43 files changed, 1054 insertions(+), 249 deletions(-) create mode 100644 snippets/go/sort_bubble_sort_test.go create mode 100644 snippets/go/sort_builtin_test.go create mode 100644 snippets/go/sort_insertion_sort_test.go create mode 100644 snippets/go/sort_merge_sort_test.go create mode 100644 snippets/go/sort_selection_sort_test.go create mode 100644 snippets/js/sort_bubble_sort.js create mode 100644 snippets/js/sort_builtin.js create mode 100644 snippets/js/sort_insertion_sort.js create mode 100644 snippets/js/sort_selection_sort.js create mode 100644 snippets/python/sort_bubble_sort.py create mode 100644 snippets/python/sort_builtin.py create mode 100644 snippets/python/sort_insertion_sort.py create mode 100644 snippets/python/sort_selection_sort.py create mode 100644 snippets/ruby/sort_bubble_sort.rb create mode 100644 snippets/ruby/sort_builtin.rb create mode 100644 snippets/ruby/sort_insertion_sort.rb create mode 100644 snippets/ruby/sort_selection_sort.rb create mode 100644 snippets/rust/sort_bubble_sort.rs create mode 100644 snippets/rust/sort_builtin.rs create mode 100644 snippets/rust/sort_insertion_sort.rs create mode 100644 snippets/rust/sort_selection_sort.rs diff --git a/snippets/go/sort_bubble_sort_test.go b/snippets/go/sort_bubble_sort_test.go new file mode 100644 index 0000000..19915e8 --- /dev/null +++ b/snippets/go/sort_bubble_sort_test.go @@ -0,0 +1,50 @@ +// bubble sort! +// +// docs: https://en.wikipedia.org/wiki/Bubble_sort +// +// bubble sort steps through a list, comparing adjacent elements and swapping them if they +// are in the wrong order. it passes through the list repeatedly until the list is sorted + +// bubble_sort is the top level function responsible for ... bubble sorting! +func bubbleSort(inputList []string) (outputList []string) { + // setup defaults + outputList = inputList + isSorted := false + + // continuously do sorting rounds as long as the list remains unsorted + for isSorted == false { + outputList, isSorted = doSortingRound(outputList) + } + + return outputList +} + +// doSortingRound does the "actual sorting" +func doSortingRound(inputList []string) (outputList []string, isSorted bool) { + isSorted = true + + for index, element := range inputList { + if index == 0 { + // we compare (index VS index - 1) so theres + // nothing to compare when looking at the 0th index + outputList = []string{element} + } else if element < outputList[index-1] { + // if this element is less than the previous element then swap their order + visitedElements := outputList[:index-1] + previousElement := outputList[index-1] + // append a list of + // - all the list elements we've visited already + // - the current element + // - the previous element + // which has the effect of swapping the order of the current and previous + // elements, while also keeping all of the visited elements in place + outputList = append(visitedElements, element, previousElement) + isSorted = false + } else { + // otherwise append + outputList = append(outputList, element) + } + } + + return outputList, isSorted +} diff --git a/snippets/go/sort_builtin_test.go b/snippets/go/sort_builtin_test.go new file mode 100644 index 0000000..67da840 --- /dev/null +++ b/snippets/go/sort_builtin_test.go @@ -0,0 +1,5 @@ +func builtinSort(inputList []string) (outputList []string) { + outputList = inputList + sort.Strings(outputList) + return outputList +} diff --git a/snippets/go/sort_insertion_sort_test.go b/snippets/go/sort_insertion_sort_test.go new file mode 100644 index 0000000..32d4d55 --- /dev/null +++ b/snippets/go/sort_insertion_sort_test.go @@ -0,0 +1,34 @@ +// insertion sort! +// +// docs: https://en.wikipedia.org/wiki/Insertion_sort +// +// Insertion sort steps through an input list, using it to grow an output list. +// On every step, it sorts the current element into its proper location on the +// output list.It continues until the input list is empty. + +func insertionSort(inputList []string) (outputList []string) { + + for idx, element := range inputList { + outputList = append(outputList, element) + outputList = sortElementAtIndex(outputList, element, idx) + } + + return outputList +} + +func sortElementAtIndex(inputList []string, element string, idx int) (outputList []string) { + outputList = inputList + targetIndex := idx + + for (targetIndex != 0) && (element < outputList[targetIndex-1]) { + outputList = swapWithPrevious(outputList, targetIndex) + targetIndex = targetIndex - 1 + } + + return outputList +} + +func swapWithPrevious(list []string, idx int) []string { + list[idx], list[idx-1] = list[idx-1], list[idx] + return list +} diff --git a/snippets/go/sort_merge_sort_test.go b/snippets/go/sort_merge_sort_test.go new file mode 100644 index 0000000..d134237 --- /dev/null +++ b/snippets/go/sort_merge_sort_test.go @@ -0,0 +1,69 @@ +// merge sort! +// +// docs: https://leetcode.com/explore/learn/card/recursion-ii/470/divide-and-conquer/2868/ + +func MergeSort(inputList []string) (sortedList []string) { + + var doMergeInput [][]string + for _, item := range inputList { + doMergeInput = append(doMergeInput, []string{item}) + } + + doMergeOutput, _ := mergeSortRound(doMergeInput, 0) + sortedList = doMergeOutput[0] + + return sortedList +} + +func mergeSortRound(inputList [][]string, inputRound int) (outputList [][]string, outputRound int) { + + // "round" is used for debugging + outputRound = inputRound + 1 + + // merge each pair, ignoring potential odd items + for i := 0; i < len(inputList)/2*2; i += 2 { + outputList = append( + outputList, + merge(inputList[i], inputList[i+1]), + ) + } + + // handle odd items + if len(inputList)%2 == 1 { + outputList = append(outputList, inputList[len(inputList)-1]) + } + + // recurse! + if len(outputList) != 1 { + return mergeSortRound(outputList, outputRound) + } + + return outputList, outputRound +} + +func merge(left []string, right []string) (result []string) { + leftIndex, rightIndex := 0, 0 + + for (leftIndex < len(left)) || (rightIndex < len(right)) { + + if leftIndex == len(left) { + // boundary check on first list, append from second list + result = append(result, right[rightIndex]) + rightIndex++ + } else if rightIndex == len(right) { + // boundary check on second list, append from first list + result = append(result, left[leftIndex]) + leftIndex++ + } else if left[leftIndex] < right[rightIndex] { + // comparison check, append from first list + result = append(result, left[leftIndex]) + leftIndex++ + } else { + // implicit comparison check, append from second list + result = append(result, right[rightIndex]) + rightIndex++ + } + } + + return result +} diff --git a/snippets/go/sort_selection_sort_test.go b/snippets/go/sort_selection_sort_test.go new file mode 100644 index 0000000..3dcf695 --- /dev/null +++ b/snippets/go/sort_selection_sort_test.go @@ -0,0 +1,37 @@ +// selection sort! +// +// docs: https://en.wikipedia.org/wiki/Selection_sort +// +// Selection sort looks through every element an input list, and finds the +// smallest element. That element is then appended to the end of an output list. +// When it reaches the end of the input list, all of the output elements will +// be sorted. + +func selectionSort(inputList []string) (sortedList []string) { + unsortedList := inputList + inputListLength := len(inputList) + + for i := 0; i < inputListLength; i++ { + smallestIndex := findSmallestIndex(unsortedList) + smallestElement := unsortedList[smallestIndex] + sortedList = append(sortedList, smallestElement) + unsortedList = removeIndex(unsortedList, smallestIndex) + } + + return sortedList +} + +func findSmallestIndex(inputList []string) (smallestIndex int) { + for index, element := range inputList { + if element < inputList[smallestIndex] { + smallestIndex = index + } + } + + return smallestIndex +} + +func removeIndex(inputList []string, index int) (outputList []string) { + outputList = append(inputList[:index], inputList[index+1:]...) + return outputList +} diff --git a/snippets/js/sort_bubble_sort.js b/snippets/js/sort_bubble_sort.js new file mode 100644 index 0000000..7e7070f --- /dev/null +++ b/snippets/js/sort_bubble_sort.js @@ -0,0 +1,35 @@ +function doSorting(inputList) { + return bubbleSort(inputList); +} + +function bubbleSort(inputList) { + outputList = inputList; + isSorted = false; + + while (isSorted === false) { + outputList, (isSorted = doSortingRound(outputList)); + } + + return outputList; +} + +function doSortingRound(inputList) { + outputList = []; + isSorted = true; + + inputList.forEach((element, index) => { + if (index === 0) { + outputList.push(element); + } else if (element < outputList[index - 1]) { + previousElement = outputList[index - 1]; + outputList.pop(); + outputList.push(element); + outputList.push(previousElement); + isSorted = false; + } else { + outputList.push(element); + } + }); + + return outputList, isSorted; +} diff --git a/snippets/js/sort_builtin.js b/snippets/js/sort_builtin.js new file mode 100644 index 0000000..fd7515c --- /dev/null +++ b/snippets/js/sort_builtin.js @@ -0,0 +1,5 @@ +function doSorting(inputList) { + outputList = inputList; + outputList.sort(); + return outputList; +} diff --git a/snippets/js/sort_insertion_sort.js b/snippets/js/sort_insertion_sort.js new file mode 100644 index 0000000..97997c3 --- /dev/null +++ b/snippets/js/sort_insertion_sort.js @@ -0,0 +1,41 @@ +// insertion sort! +// +// docs: https://en.wikipedia.org/wiki/Insertion_sort +// +// Insertion sort steps through an input list, using it to grow an output list. +// On every step, it sorts the current element into its proper location on the +// output list.It continues until the input list is empty. + +function doSorting(inputList) { + return insertionSort(inputList); +} + +function insertionSort(inputList) { + outputList = []; + + inputList.forEach((element, idx) => { + outputList.push(element); + outputList = sortElementAtIndex(outputList, element, idx); + }); + + return outputList; +} + +function sortElementAtIndex(inputList, element, idx) { + target_index = idx; + outputList = inputList; + + while (target_index != 0 && element < outputList[target_index - 1]) { + swapWithPrevious(outputList, target_index); + target_index -= 1; + } + + return outputList; +} + +function swapWithPrevious(list, idx) { + tmp = list[idx - 1]; + list[idx - 1] = list[idx]; + list[idx] = tmp; + return list; +} diff --git a/snippets/js/sort_selection_sort.js b/snippets/js/sort_selection_sort.js new file mode 100644 index 0000000..19d1fce --- /dev/null +++ b/snippets/js/sort_selection_sort.js @@ -0,0 +1,48 @@ +// selection sort! +// +// docs: https://en.wikipedia.org/wiki/Selection_sort +// +// Selection sort looks through every element an input list, and finds the +// smallest element. That element is then appended to the end of an output list. +// When it reaches the end of the input list, all of the output elements will +// be sorted. + +function doSorting(inputList) { + return selectionSort(inputList); +} + +function selectionSort(inputList) { + sortedElements = []; + unsortedElements = inputList; + inputListLength = inputList.length; + + for (let index = 0; index < inputListLength; index++) { + // console.log("--- for (let index = 0; index < inputList.length; index++) { ---"); + // console.log("unsortedElements => " + unsortedElements); + // console.log("sortedElements => " + sortedElements); + + smallestIndex = findSmallestIndex(unsortedElements); + smallestElement = unsortedElements[smallestIndex]; + sortedElements.push(smallestElement); + unsortedElements.splice(smallestIndex, 1); + + // console.log("smallestIndex => " + smallestIndex); + // console.log("smallestElement => " + smallestElement); + // console.log("unsortedElements => " + unsortedElements); + // console.log("sortedElements => " + sortedElements); + } + + return sortedElements; +} + +function findSmallestIndex(inputList) { + smallestIndex = 0; + + inputList.forEach((element, index) => { + if (element < inputList[smallestIndex]) { + smallestIndex = index; + } + }); + + return smallestIndex; +} diff --git a/snippets/python/sort_bubble_sort.py b/snippets/python/sort_bubble_sort.py new file mode 100644 index 0000000..44edc3c --- /dev/null +++ b/snippets/python/sort_bubble_sort.py @@ -0,0 +1,54 @@ +# bubble sort! +# +# docs: https://en.wikipedia.org/wiki/Bubble_sort +# +# bubble sort steps through a list, comparing adjacent elements and swapping them if they +# are in the wrong order. it passes through the list repeatedly until the list is sorted + + +def do_sorting(input_list): + return bubble_sort(input_list) + + +# bubble_sort is the top level function responsible for ... bubble sorting! +def bubble_sort(input_list: list[str]) -> list[str]: + # set defaults + output_list = input_list + is_sorted = False + + # continuously do sorting rounds as long as the list remains unsorted + while is_sorted is False: + output_list, is_sorted = do_sorting_round(output_list) + + # mission accomplished! āœØ + return output_list + + +# do_sorting_round does the "actual sorting" +def do_sorting_round(input_list: list[str]) -> (list[str], bool): + # set defaults + output_list = [] + is_sorted = True + + for index, element in enumerate(input_list): + # we compare (index VS index - 1) so there's + # nothing to compare when looking at the 0th index + if index == 0: + output_list.append(element) + continue + + # grab (index - 1) + previous_element = output_list[index - 1] + + # if this element is less than the previous element then swap their order + if element < previous_element: + output_list.pop() + output_list.append(element) + output_list.append(previous_element) + is_sorted = False + # otherwise append + else: + output_list.append(element) + + return output_list, is_sorted + diff --git a/snippets/python/sort_builtin.py b/snippets/python/sort_builtin.py new file mode 100644 index 0000000..bcfb268 --- /dev/null +++ b/snippets/python/sort_builtin.py @@ -0,0 +1,4 @@ + +def do_sorting(input_list): + return sorted(input_list) + diff --git a/snippets/python/sort_insertion_sort.py b/snippets/python/sort_insertion_sort.py new file mode 100644 index 0000000..1a80659 --- /dev/null +++ b/snippets/python/sort_insertion_sort.py @@ -0,0 +1,37 @@ +# insertion sort! +# +# docs: https://en.wikipedia.org/wiki/Insertion_sort +# +# insertion sort steps through an input list, using it to grow an output list. +# on every step, it inserts the current element into its proper location on the output list. +# it continues until the input list is empty. + + +def do_sorting(input_list): + return insertion_sort(input_list) + + +def insertion_sort(input_list: list[str]) -> list[str]: + output_list = [] + + for index, element in enumerate(input_list): + output_list.append(element) + output_list = insert_element(output_list, element, index) + + return output_list + + +def insert_element(input_list: list[str], element: str, index: int) -> list[str]: + output_list = input_list + target_index = index + + while (target_index != 0) and (element < output_list[target_index - 1]): + # swap order + output_list[target_index], output_list[target_index - 1] = ( + output_list[target_index - 1], + output_list[target_index], + ) + target_index = target_index - 1 + + return output_list + diff --git a/snippets/python/sort_selection_sort.py b/snippets/python/sort_selection_sort.py new file mode 100644 index 0000000..f6d4065 --- /dev/null +++ b/snippets/python/sort_selection_sort.py @@ -0,0 +1,47 @@ +# selection sort! +# +# docs: https://en.wikipedia.org/wiki/Selection_sort +# +# Selection sort looks through every element an input list, and finds the smallest element. +# That element is then appended to the end of an output list. Which it reaches the end +# of the input list, all of the output elements will be sorted. + + +def do_sorting(input_list): + return selection_sort(input_list) + + +def selection_sort(input_list): + sorted_elements = [] + unsorted_elements = input_list + + # do: iterate over the length of the unsorted_elements + # do not: iterate over the unsorted_elements list directly + # why: + # it's risky, because some languages (like python) WILL for sure + # stop list iteration early because of you popping an element + # out of the list + for _ in range(len(unsorted_elements)): + # print("--- for _ in unsorted_elements: ---") + # print(f"unsorted_elements => {unsorted_elements}") + # print(f"sorted_elements => {sorted_elements}") + smallest_index = find_smallest_index(unsorted_elements) + smallest_element = unsorted_elements[smallest_index] + sorted_elements.append(smallest_element) + unsorted_elements.pop(smallest_index) + # print(f"smallest_index => {smallest_index}") + # print(f"sorted_elements => {sorted_elements}") + # print(f"unsorted_elements => {unsorted_elements}") + + return sorted_elements + + +def find_smallest_index(input_list): + smallest_index = 0 + + for index, element in enumerate(input_list): + if element < input_list[smallest_index]: + smallest_index = index + + return smallest_index + diff --git a/snippets/ruby/sort_bubble_sort.rb b/snippets/ruby/sort_bubble_sort.rb new file mode 100644 index 0000000..d43707c --- /dev/null +++ b/snippets/ruby/sort_bubble_sort.rb @@ -0,0 +1,35 @@ +def do_sorting(input_list) + bubble_sort(input_list) +end + +def bubble_sort(input_list) + output_list = input_list + is_sorted = false + + while is_sorted == false + output_list, is_sorted = do_sorting_round(output_list) + end + + output_list +end + +def do_sorting_round(input_list) + output_list = [] + is_sorted = true + + input_list.each_with_index do |element, index| + if index.zero? + output_list.append(element) + elsif element < output_list[index - 1] + previous_element = output_list[index - 1] + output_list.pop + output_list.append(element) + output_list.append(previous_element) + is_sorted = false + else + output_list.append(element) + end + end + + [output_list, is_sorted] +end diff --git a/snippets/ruby/sort_builtin.rb b/snippets/ruby/sort_builtin.rb new file mode 100644 index 0000000..2814163 --- /dev/null +++ b/snippets/ruby/sort_builtin.rb @@ -0,0 +1,5 @@ +def do_sorting(input_list) + output_list = input_list + output_list = output_list.sort + output_list +end diff --git a/snippets/ruby/sort_insertion_sort.rb b/snippets/ruby/sort_insertion_sort.rb new file mode 100644 index 0000000..ef8711a --- /dev/null +++ b/snippets/ruby/sort_insertion_sort.rb @@ -0,0 +1,46 @@ +# insertion sort! +# +# docs: https://en.wikipedia.org/wiki/Insertion_sort +# +# Insertion sort steps through an input list, using it to grow an output list. +# On every step, it sorts the current element into its proper location on the +# output list. It continues until the input list is empty. + +def do_sorting(input_list) + insertion_sort(input_list) +end + +def insertion_sort(input_list) + output_list = [] + + input_list.each_with_index do |element, idx| + output_list.append(element) + output_list = sort_element_at_index(output_list, element, idx) + end + + output_list +end + +def sort_element_at_index(input_list, element, idx) + # puts '--- sort_element_at_index ---' + # puts "element => #{element}" + # puts "index => #{idx}" + output_list = input_list + target_index = idx + + while (target_index != 0) && (element < output_list[target_index - 1]) + output_list.swap(target_index) + target_index -= 1 + # puts "\ttarget_index => #{target_index}" + # puts "\toutput_list => #{output_list}" + end + + output_list +end + +# add swap method to array +class Array + def swap(idx) + self[idx], self[idx - 1] = self[idx - 1], self[idx] + end +end diff --git a/snippets/ruby/sort_selection_sort.rb b/snippets/ruby/sort_selection_sort.rb new file mode 100644 index 0000000..cfedd16 --- /dev/null +++ b/snippets/ruby/sort_selection_sort.rb @@ -0,0 +1,47 @@ +# selection sort! +# +# docs: https://en.wikipedia.org/wiki/Selection_sort +# +# Selection sort looks through every element an input list, and finds the +# smallest element. That element is then appended to the end of an output list. +# When it reaches the end of the input list, all of the output elements will +# be sorted. + +def do_sorting(input_list) + selection_sort(input_list) +end + +def selection_sort(input_list) + sorted_elements = [] + unsorted_elements = input_list + + (0..unsorted_elements.length).each do |_| + # puts '--- (0..unsorted_elements.length).each do |_| ---' + # puts "unsorted_elements #{unsorted_elements}" + # puts "sorted_elements #{sorted_elements}" + + smallest_index = find_smallest_index(unsorted_elements) + smallest_element = unsorted_elements[smallest_index] + sorted_elements.append(smallest_element) + # ruby `pop` DOES NOT work the same as python `pop` + # for python `pop` in ruby, use `delete_at` + unsorted_elements.delete_at(smallest_index) + + # puts "smallest_index #{smallest_index}" + # puts "smallest_element #{smallest_element}" + # puts "unsorted_elements #{unsorted_elements}" + # puts "sorted_elements #{sorted_elements}" + end + + sorted_elements +end + +def find_smallest_index(input_list) + smallest_index = 0 + + input_list.each_with_index do |element, index| + smallest_index = index if element < input_list[smallest_index] + end + + smallest_index +end diff --git a/snippets/rust/sort_bubble_sort.rs b/snippets/rust/sort_bubble_sort.rs new file mode 100644 index 0000000..8cbd1a9 --- /dev/null +++ b/snippets/rust/sort_bubble_sort.rs @@ -0,0 +1,37 @@ +fn do_sorting(input_list: Vec<&str>) -> Vec<&str> { + return bubble_sort(input_list); +} + +fn bubble_sort(input_list: Vec<&str>) -> Vec<&str> { + let mut output_list = input_list; + let mut is_sorted = false; + + while is_sorted == false { + let (_output_list, _is_sorted) = do_sorting_round(output_list); + output_list = _output_list; + is_sorted = _is_sorted; + } + + return output_list; +} + +fn do_sorting_round(input_list: Vec<&str>) -> (Vec<&str>, bool) { + let mut output_list: Vec<&str> = vec![]; + let mut is_sorted = true; + + for (index, element) in input_list.iter().enumerate() { + if index == 0 { + output_list.push(element); + } else if element < &output_list[index - 1] { + let previous_element = output_list[index - 1]; + output_list.pop(); + output_list.push(element); + output_list.push(previous_element); + is_sorted = false + } else { + output_list.push(element); + } + } + + return (output_list, is_sorted); +} diff --git a/snippets/rust/sort_builtin.rs b/snippets/rust/sort_builtin.rs new file mode 100644 index 0000000..ead76f7 --- /dev/null +++ b/snippets/rust/sort_builtin.rs @@ -0,0 +1,5 @@ +fn do_sorting(input_list: Vec<&str>) -> Vec<&str> { + let mut output_list = input_list; + output_list.sort(); + return output_list; +} diff --git a/snippets/rust/sort_insertion_sort.rs b/snippets/rust/sort_insertion_sort.rs new file mode 100644 index 0000000..f8a54ca --- /dev/null +++ b/snippets/rust/sort_insertion_sort.rs @@ -0,0 +1,28 @@ +// insertion sort! +// +// docs: https://en.wikipedia.org/wiki/Insertion_sort +// +// insertion sort steps through an input list, using it to grow an output list. +// on every step, it inserts the current element into its proper location on the output list. +// it continues until the input list is empty. + +fn do_sorting(input_list: Vec<&str>) -> Vec<&str> { + return insertion_sort(input_list); +} + +fn insertion_sort(input_list: Vec<&str>) -> Vec<&str> { + let mut output_list: Vec<&str> = vec![]; + + for (index, element) in input_list.iter().enumerate() { + output_list.push(element); + if index != 0 && element < &output_list[index - 1] { + let mut target_index = index; + while target_index != 0 && element < &output_list[target_index - 1] { + output_list.swap(target_index, target_index - 1); + target_index = target_index - 1; + } + } + } + + return output_list; +} diff --git a/snippets/rust/sort_selection_sort.rs b/snippets/rust/sort_selection_sort.rs new file mode 100644 index 0000000..5aa5450 --- /dev/null +++ b/snippets/rust/sort_selection_sort.rs @@ -0,0 +1,37 @@ +// selection sort! +// +// docs: https://en.wikipedia.org/wiki/Selection_sort +// +// Selection sort looks through every element an input list, and finds the smallest element. +// That element is then appended to the end of an output list. Which it reaches the end +// of the input list, all of the output elements will be sorted. + +fn do_sorting(input_list: Vec<&str>) -> Vec<&str> { + return selection_sort(input_list); +} + +fn selection_sort(input_list: Vec<&str>) -> Vec<&str> { + let mut sorted_elements: Vec<&str> = vec![]; + let mut unsorted_elements: Vec<&str> = input_list; + + for _ in 0..unsorted_elements.len() { + let smallest_index = find_smallest_index(&unsorted_elements); + let smallest_element = unsorted_elements[smallest_index]; + sorted_elements.push(smallest_element); + unsorted_elements.remove(smallest_index); + } + + return sorted_elements; +} + +fn find_smallest_index(input_list: &Vec<&str>) -> usize { + let mut smallest_index: usize = 0; + + for (index, element) in input_list.iter().enumerate() { + if element < &input_list[smallest_index] { + smallest_index = index; + } + } + + return smallest_index; +} diff --git a/src/go/sort_bubble_sort_test.go b/src/go/sort_bubble_sort_test.go index 84065cb..1cb7e9a 100644 --- a/src/go/sort_bubble_sort_test.go +++ b/src/go/sort_bubble_sort_test.go @@ -4,9 +4,9 @@ import ( "testing" ) -/////////////////////// -// sort script start // -/////////////////////// +////////////////////////// +// business logic start // +////////////////////////// // bubble sort! // @@ -59,9 +59,9 @@ func doSortingRound(inputList []string) (outputList []string, isSorted bool) { return outputList, isSorted } -///////////////////// -// sort script end // -///////////////////// +//////////////////////// +// business logic end // +//////////////////////// func TestBubbleSort(t *testing.T) { t.Run("test", func(t *testing.T) { diff --git a/src/go/sort_builtin_test.go b/src/go/sort_builtin_test.go index 88138a0..ce5f777 100644 --- a/src/go/sort_builtin_test.go +++ b/src/go/sort_builtin_test.go @@ -5,9 +5,9 @@ import ( "testing" ) -/////////////////////// -// sort script start // -/////////////////////// +////////////////////////// +// business logic start // +////////////////////////// func builtinSort(inputList []string) (outputList []string) { outputList = inputList @@ -15,9 +15,9 @@ func builtinSort(inputList []string) (outputList []string) { return outputList } -///////////////////// -// sort script end // -///////////////////// +//////////////////////// +// business logic end // +//////////////////////// func TestBuiltinSort(t *testing.T) { t.Run("test", func(t *testing.T) { diff --git a/src/go/sort_insertion_sort_test.go b/src/go/sort_insertion_sort_test.go index f41c36c..88905e2 100644 --- a/src/go/sort_insertion_sort_test.go +++ b/src/go/sort_insertion_sort_test.go @@ -4,9 +4,9 @@ import ( "testing" ) -/////////////////////// -// sort script start // -/////////////////////// +////////////////////////// +// business logic start // +////////////////////////// // insertion sort! // @@ -43,9 +43,9 @@ func swapWithPrevious(list []string, idx int) []string { return list } -///////////////////// -// sort script end // -///////////////////// +//////////////////////// +// business logic end // +//////////////////////// func TestInsertionSort(t *testing.T) { t.Run("test", func(t *testing.T) { diff --git a/src/go/sort_merge_sort_test.go b/src/go/sort_merge_sort_test.go index bf76e4a..59f6346 100644 --- a/src/go/sort_merge_sort_test.go +++ b/src/go/sort_merge_sort_test.go @@ -4,9 +4,9 @@ import ( "testing" ) -/////////////////////// -// sort script start // -/////////////////////// +////////////////////////// +// business logic start // +////////////////////////// // merge sort! // @@ -78,9 +78,9 @@ func merge(left []string, right []string) (result []string) { return result } -///////////////////// -// sort script end // -///////////////////// +//////////////////////// +// business logic end // +//////////////////////// func TestMergeSort(t *testing.T) { t.Run("test", func(t *testing.T) { diff --git a/src/go/sort_selection_sort_test.go b/src/go/sort_selection_sort_test.go index 90a3179..d4593c3 100644 --- a/src/go/sort_selection_sort_test.go +++ b/src/go/sort_selection_sort_test.go @@ -4,9 +4,9 @@ import ( "testing" ) -/////////////////////// -// sort script start // -/////////////////////// +////////////////////////// +// business logic start // +////////////////////////// // selection sort! // @@ -46,9 +46,9 @@ func removeIndex(inputList []string, index int) (outputList []string) { return outputList } -///////////////////// -// sort script end // -///////////////////// +//////////////////////// +// business logic end // +//////////////////////// func TestSelectionSort(t *testing.T) { t.Run("test", func(t *testing.T) { diff --git a/src/js/sort_bubble_sort.js b/src/js/sort_bubble_sort.js index df9f47e..fb8ca30 100644 --- a/src/js/sort_bubble_sort.js +++ b/src/js/sort_bubble_sort.js @@ -1,8 +1,8 @@ -var fs = require('fs'); +var fs = require("fs"); -/////////////////////// -// sort script start // -/////////////////////// +////////////////////////// +// business logic start // +////////////////////////// function doSorting(inputList) { return bubbleSort(inputList); @@ -13,10 +13,10 @@ function bubbleSort(inputList) { isSorted = false; while (isSorted === false) { - outputList, isSorted = doSortingRound(outputList); + outputList, (isSorted = doSortingRound(outputList)); } - return outputList + return outputList; } function doSortingRound(inputList) { @@ -31,7 +31,7 @@ function doSortingRound(inputList) { outputList.pop(); outputList.push(element); outputList.push(previousElement); - isSorted = false + isSorted = false; } else { outputList.push(element); } @@ -40,15 +40,15 @@ function doSortingRound(inputList) { return outputList, isSorted; } -///////////////////// -// sort script end // -///////////////////// +//////////////////////// +// business logic end // +//////////////////////// // šŸ‘‡šŸ½ copy pasted helpers // read input file let inputFilePath = process.env.INPUT_PATH; -let inputString = fs.readFileSync(inputFilePath, 'utf8'); +let inputString = fs.readFileSync(inputFilePath, "utf8"); let inputStringSplit = inputString.split(/\n/); // clean input data diff --git a/src/js/sort_builtin.js b/src/js/sort_builtin.js index 25c6a0b..7f8b5f9 100644 --- a/src/js/sort_builtin.js +++ b/src/js/sort_builtin.js @@ -1,8 +1,8 @@ -var fs = require('fs'); +var fs = require("fs"); -/////////////////////// -// sort script start // -/////////////////////// +////////////////////////// +// business logic start // +////////////////////////// function doSorting(inputList) { outputList = inputList; @@ -10,15 +10,15 @@ function doSorting(inputList) { return outputList; } -///////////////////// -// sort script end // -///////////////////// +//////////////////////// +// business logic end // +//////////////////////// // šŸ‘‡šŸ½ copy pasted helpers // read input file let inputFilePath = process.env.INPUT_PATH; -let inputString = fs.readFileSync(inputFilePath, 'utf8'); +let inputString = fs.readFileSync(inputFilePath, "utf8"); let inputStringSplit = inputString.split(/\n/); // clean input data diff --git a/src/js/sort_insertion_sort.js b/src/js/sort_insertion_sort.js index 784815a..81c1d42 100644 --- a/src/js/sort_insertion_sort.js +++ b/src/js/sort_insertion_sort.js @@ -1,8 +1,8 @@ -var fs = require('fs'); +var fs = require("fs"); -/////////////////////// -// sort script start // -/////////////////////// +////////////////////////// +// business logic start // +////////////////////////// // insertion sort! // @@ -20,44 +20,41 @@ function insertionSort(inputList) { outputList = []; inputList.forEach((element, idx) => { - outputList.push(element) - outputList = sortElementAtIndex(outputList, element, idx) + outputList.push(element); + outputList = sortElementAtIndex(outputList, element, idx); }); - return outputList + return outputList; } function sortElementAtIndex(inputList, element, idx) { target_index = idx; outputList = inputList; - while ( - (target_index != 0) && - (element < outputList[target_index - 1]) - ) { - swapWithPrevious(outputList, target_index) - target_index -= 1 + while (target_index != 0 && element < outputList[target_index - 1]) { + swapWithPrevious(outputList, target_index); + target_index -= 1; } - return outputList + return outputList; } function swapWithPrevious(list, idx) { - tmp = list[idx - 1] - list[idx - 1] = list[idx] - list[idx] = tmp - return list + tmp = list[idx - 1]; + list[idx - 1] = list[idx]; + list[idx] = tmp; + return list; } -///////////////////// -// sort script end // -///////////////////// +//////////////////////// +// business logic end // +//////////////////////// // šŸ‘‡šŸ½ copy pasted helpers // read input file let inputFilePath = process.env.INPUT_PATH; -let inputString = fs.readFileSync(inputFilePath, 'utf8'); +let inputString = fs.readFileSync(inputFilePath, "utf8"); let inputStringSplit = inputString.split(/\n/); // clean input data diff --git a/src/js/sort_selection_sort.js b/src/js/sort_selection_sort.js index 65e6fc2..b01f855 100644 --- a/src/js/sort_selection_sort.js +++ b/src/js/sort_selection_sort.js @@ -1,8 +1,8 @@ -var fs = require('fs'); +var fs = require("fs"); -/////////////////////// -// sort script start // -/////////////////////// +////////////////////////// +// business logic start // +////////////////////////// // selection sort! // @@ -53,15 +53,15 @@ function findSmallestIndex(inputList) { return smallestIndex; } -///////////////////// -// sort script end // -///////////////////// +//////////////////////// +// business logic end // +//////////////////////// // šŸ‘‡šŸ½ copy pasted helpers // read input file let inputFilePath = process.env.INPUT_PATH; -let inputString = fs.readFileSync(inputFilePath, 'utf8'); +let inputString = fs.readFileSync(inputFilePath, "utf8"); let inputStringSplit = inputString.split(/\n/); // clean input data diff --git a/src/python/sort_bubble_sort.py b/src/python/sort_bubble_sort.py index 0cc1397..8dfc9f1 100644 --- a/src/python/sort_bubble_sort.py +++ b/src/python/sort_bubble_sort.py @@ -1,9 +1,9 @@ import helpers -##################### -# sort script start # -##################### +######################## +# business logic start # +######################## # bubble sort! # @@ -60,11 +60,9 @@ def do_sorting_round(input_list: list[str]) -> (list[str], bool): return output_list, is_sorted -################### -# sort script end # -################### - -# šŸ‘‡šŸ½ copy pasted helpers +###################### +# business logic end # +###################### if __name__ == "__main__": helpers.run(do_sorting) diff --git a/src/python/sort_builtin.py b/src/python/sort_builtin.py index 6941b84..66af60c 100644 --- a/src/python/sort_builtin.py +++ b/src/python/sort_builtin.py @@ -1,20 +1,18 @@ import helpers -##################### -# sort script start # -##################### +######################## +# business logic start # +######################## def do_sorting(input_list): return sorted(input_list) -################### -# sort script end # -################### - -# šŸ‘‡šŸ½ copy pasted helpers +###################### +# business logic end # +###################### if __name__ == "__main__": helpers.run(do_sorting) diff --git a/src/python/sort_insertion_sort.py b/src/python/sort_insertion_sort.py index 13afca8..0c2c714 100644 --- a/src/python/sort_insertion_sort.py +++ b/src/python/sort_insertion_sort.py @@ -1,9 +1,9 @@ import helpers -##################### -# sort script start # -##################### +######################## +# business logic start # +######################## # insertion sort! # @@ -43,11 +43,9 @@ def insert_element(input_list: list[str], element: str, index: int) -> list[str] return output_list -################### -# sort script end # -################### - -# šŸ‘‡šŸ½ copy pasted helpers +###################### +# business logic end # +###################### if __name__ == "__main__": helpers.run(do_sorting) diff --git a/src/python/sort_selection_sort.py b/src/python/sort_selection_sort.py index 72d4f68..8a78b2c 100644 --- a/src/python/sort_selection_sort.py +++ b/src/python/sort_selection_sort.py @@ -1,9 +1,9 @@ import helpers -##################### -# sort script start # -##################### +######################## +# business logic start # +######################## # selection sort! # @@ -53,11 +53,9 @@ def find_smallest_index(input_list): return smallest_index -################### -# sort script end # -################### - -# šŸ‘‡šŸ½ copy pasted helpers +###################### +# business logic end # +###################### if __name__ == "__main__": helpers.run(do_sorting) diff --git a/src/ruby/sort_bubble_sort.rb b/src/ruby/sort_bubble_sort.rb index 7636e65..84f4e29 100644 --- a/src/ruby/sort_bubble_sort.rb +++ b/src/ruby/sort_bubble_sort.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -##################### -# sort script start # -##################### +######################## +# business logic start # +######################## def do_sorting(input_list) bubble_sort(input_list) @@ -40,9 +40,9 @@ def do_sorting_round(input_list) [output_list, is_sorted] end -################### -# sort script end # -################### +###################### +# business logic end # +###################### # šŸ‘‡šŸ½ copy pasted helpers diff --git a/src/ruby/sort_builtin.rb b/src/ruby/sort_builtin.rb index e10a426..be6dabf 100644 --- a/src/ruby/sort_builtin.rb +++ b/src/ruby/sort_builtin.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -##################### -# sort script start # -##################### +######################## +# business logic start # +######################## def do_sorting(input_list) output_list = input_list @@ -10,9 +10,9 @@ def do_sorting(input_list) output_list end -################### -# sort script end # -################### +###################### +# business logic end # +###################### # šŸ‘‡šŸ½ copy pasted helpers diff --git a/src/ruby/sort_insertion_sort.rb b/src/ruby/sort_insertion_sort.rb index bf76fcb..4154a01 100644 --- a/src/ruby/sort_insertion_sort.rb +++ b/src/ruby/sort_insertion_sort.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -##################### -# sort script start # -##################### +######################## +# business logic start # +######################## # insertion sort! # @@ -51,9 +51,9 @@ def swap(idx) end end -################### -# sort script end # -################### +###################### +# business logic end # +###################### # šŸ‘‡šŸ½ copy pasted helpers diff --git a/src/ruby/sort_selection_sort.rb b/src/ruby/sort_selection_sort.rb index 14370e6..2fd2c3f 100644 --- a/src/ruby/sort_selection_sort.rb +++ b/src/ruby/sort_selection_sort.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -##################### -# sort script start # -##################### +######################## +# business logic start # +######################## # selection sort! # @@ -52,9 +52,9 @@ def find_smallest_index(input_list) smallest_index end -################### -# sort script end # -################### +###################### +# business logic end # +###################### # šŸ‘‡šŸ½ copy pasted helpers diff --git a/src/rust/sort_bubble_sort.rs b/src/rust/sort_bubble_sort.rs index bb7e08f..4f5a144 100644 --- a/src/rust/sort_bubble_sort.rs +++ b/src/rust/sort_bubble_sort.rs @@ -3,9 +3,9 @@ use std::fs; use std::fs::File; use std::io::Write; -/////////////////////// -// sort script start // -/////////////////////// +////////////////////////// +// business logic start // +////////////////////////// fn do_sorting(input_list: Vec<&str>) -> Vec<&str> { return bubble_sort(input_list); @@ -45,9 +45,9 @@ fn do_sorting_round(input_list: Vec<&str>) -> (Vec<&str>, bool) { return (output_list, is_sorted); } -///////////////////// -// sort script end // -///////////////////// +//////////////////////// +// business logic end // +//////////////////////// // šŸ‘‡šŸ½ copy pasted helpers diff --git a/src/rust/sort_builtin.rs b/src/rust/sort_builtin.rs index 8ab6668..99533b0 100644 --- a/src/rust/sort_builtin.rs +++ b/src/rust/sort_builtin.rs @@ -3,9 +3,9 @@ use std::fs; use std::fs::File; use std::io::Write; -/////////////////////// -// sort script start // -/////////////////////// +////////////////////////// +// business logic start // +////////////////////////// fn do_sorting(input_list: Vec<&str>) -> Vec<&str> { let mut output_list = input_list; @@ -13,9 +13,9 @@ fn do_sorting(input_list: Vec<&str>) -> Vec<&str> { return output_list; } -///////////////////// -// sort script end // -///////////////////// +//////////////////////// +// business logic end // +//////////////////////// // šŸ‘‡šŸ½ copy pasted helpers diff --git a/src/rust/sort_insertion_sort.rs b/src/rust/sort_insertion_sort.rs index b2c8713..543dab5 100644 --- a/src/rust/sort_insertion_sort.rs +++ b/src/rust/sort_insertion_sort.rs @@ -3,9 +3,9 @@ use std::fs; use std::fs::File; use std::io::Write; -/////////////////////// -// sort script start // -/////////////////////// +////////////////////////// +// business logic start // +////////////////////////// // insertion sort! // @@ -16,51 +16,51 @@ use std::io::Write; // it continues until the input list is empty. fn do_sorting(input_list: Vec<&str>) -> Vec<&str> { - return insertion_sort(input_list); + return insertion_sort(input_list); } fn insertion_sort(input_list: Vec<&str>) -> Vec<&str> { - let mut output_list: Vec<&str> = vec![]; + let mut output_list: Vec<&str> = vec![]; - for (index, element) in input_list.iter().enumerate() { - output_list.push(element); - if index != 0 && element < &output_list[index - 1] { - let mut target_index = index; - while target_index != 0 && element < &output_list[target_index - 1] { - output_list.swap(target_index, target_index - 1); - target_index = target_index - 1; - } + for (index, element) in input_list.iter().enumerate() { + output_list.push(element); + if index != 0 && element < &output_list[index - 1] { + let mut target_index = index; + while target_index != 0 && element < &output_list[target_index - 1] { + output_list.swap(target_index, target_index - 1); + target_index = target_index - 1; + } + } } - } - return output_list; + return output_list; } -///////////////////// -// sort script end // -///////////////////// +//////////////////////// +// business logic end // +//////////////////////// // šŸ‘‡šŸ½ copy pasted helpers fn main() -> Result<(), Box> { - // setup - let input_file_path = env::var("INPUT_PATH").unwrap(); - let output_file_path = env::var("OUTPUT_PATH").unwrap(); + // setup + let input_file_path = env::var("INPUT_PATH").unwrap(); + let output_file_path = env::var("OUTPUT_PATH").unwrap(); - // get input data - let input_data_string = fs::read_to_string(input_file_path)?; - let input_data_vec = input_data_string.lines().collect(); + // get input data + let input_data_string = fs::read_to_string(input_file_path)?; + let input_data_vec = input_data_string.lines().collect(); - // do sorting - let sorted_data = do_sorting(input_data_vec); + // do sorting + let sorted_data = do_sorting(input_data_vec); - // write output data - // join on \n, and add an additional trailing \n - let output_data_string = sorted_data.join("\n") + "\n"; - let output_data_bytes = output_data_string.as_bytes(); - let mut output_file = File::create(output_file_path)?; - output_file.write_all(output_data_bytes)?; + // write output data + // join on \n, and add an additional trailing \n + let output_data_string = sorted_data.join("\n") + "\n"; + let output_data_bytes = output_data_string.as_bytes(); + let mut output_file = File::create(output_file_path)?; + output_file.write_all(output_data_bytes)?; - // teardown - Ok(()) + // teardown + Ok(()) } diff --git a/src/rust/sort_selection_sort.rs b/src/rust/sort_selection_sort.rs index a022830..dd81e5e 100644 --- a/src/rust/sort_selection_sort.rs +++ b/src/rust/sort_selection_sort.rs @@ -3,9 +3,9 @@ use std::fs; use std::fs::File; use std::io::Write; -/////////////////////// -// sort script start // -/////////////////////// +////////////////////////// +// business logic start // +////////////////////////// // selection sort! // @@ -16,60 +16,60 @@ use std::io::Write; // of the input list, all of the output elements will be sorted. fn do_sorting(input_list: Vec<&str>) -> Vec<&str> { - return selection_sort(input_list); + return selection_sort(input_list); } fn selection_sort(input_list: Vec<&str>) -> Vec<&str> { - let mut sorted_elements: Vec<&str> = vec![]; - let mut unsorted_elements: Vec<&str> = input_list; - - for _ in 0..unsorted_elements.len() { - let smallest_index = find_smallest_index(&unsorted_elements); - let smallest_element = unsorted_elements[smallest_index]; - sorted_elements.push(smallest_element); - unsorted_elements.remove(smallest_index); - } + let mut sorted_elements: Vec<&str> = vec![]; + let mut unsorted_elements: Vec<&str> = input_list; + + for _ in 0..unsorted_elements.len() { + let smallest_index = find_smallest_index(&unsorted_elements); + let smallest_element = unsorted_elements[smallest_index]; + sorted_elements.push(smallest_element); + unsorted_elements.remove(smallest_index); + } - return sorted_elements; + return sorted_elements; } fn find_smallest_index(input_list: &Vec<&str>) -> usize { - let mut smallest_index: usize = 0; + let mut smallest_index: usize = 0; - for (index, element) in input_list.iter().enumerate() { - if element < &input_list[smallest_index] { - smallest_index = index; + for (index, element) in input_list.iter().enumerate() { + if element < &input_list[smallest_index] { + smallest_index = index; + } } - } - return smallest_index; + return smallest_index; } -///////////////////// -// sort script end // -///////////////////// +//////////////////////// +// business logic end // +//////////////////////// // šŸ‘‡šŸ½ copy pasted helpers fn main() -> Result<(), Box> { - // setup - let input_file_path = env::var("INPUT_PATH").unwrap(); - let output_file_path = env::var("OUTPUT_PATH").unwrap(); - - // get input data - let input_data_string = fs::read_to_string(input_file_path)?; - let input_data_vec = input_data_string.lines().collect(); - - // do sorting - let sorted_data = do_sorting(input_data_vec); - - // write output data - // join on \n, and add an additional trailing \n - let output_data_string = sorted_data.join("\n") + "\n"; - let output_data_bytes = output_data_string.as_bytes(); - let mut output_file = File::create(output_file_path)?; - output_file.write_all(output_data_bytes)?; - - // teardown - Ok(()) + // setup + let input_file_path = env::var("INPUT_PATH").unwrap(); + let output_file_path = env::var("OUTPUT_PATH").unwrap(); + + // get input data + let input_data_string = fs::read_to_string(input_file_path)?; + let input_data_vec = input_data_string.lines().collect(); + + // do sorting + let sorted_data = do_sorting(input_data_vec); + + // write output data + // join on \n, and add an additional trailing \n + let output_data_string = sorted_data.join("\n") + "\n"; + let output_data_bytes = output_data_string.as_bytes(); + let mut output_file = File::create(output_file_path)?; + output_file.write_all(output_data_bytes)?; + + // teardown + Ok(()) } diff --git a/tasks.py b/tasks.py index 9e61d3e..7b53dee 100644 --- a/tasks.py +++ b/tasks.py @@ -6,6 +6,7 @@ import dataclasses import json import time +import traceback # 3rd party packages import yaml @@ -37,13 +38,17 @@ def clean_string(inp): @dataclasses.dataclass class TestRunnerContext: + language: str script_name: str docker_pull: str docker_run_test: str script_relative_path: str + script_name_with_file_type: str script_output_file_path: str script_output_file_name: str prepared_file_path: str + snippet_start_line: int + snippet_end_line: int @property def data(self): @@ -54,6 +59,9 @@ class TestRunnerContexts: base_directory = os.getcwd() data_folder_path = "./data" ctxs: list[TestRunnerContext] = [] + # We use these strings to mark the start and end of the important part of our scripts + snippet_start_text = "business logic start" + snippet_end_text = "business logic end" def __init__(self, language) -> None: # get the language specific config @@ -146,35 +154,72 @@ def generate(self, language, config, script_path): # construct docker pull command docker_pull = f"docker pull --quiet {config['dockerImage']}" + # get the script contents + with open(script_relative_path, "r", encoding="utf-8") as obj: + script_contents = obj.readlines() + + # find the start and end lines of the script + # the start line is the location of the start text, plus 3 lines + # the end line is the location of the end text, minus 3 lines + snippet_start_line = 0 + snippet_start_line_offset = 3 + snippet_end_line = 0 + snippet_end_line_offset = 2 + for idx, line in enumerate(script_contents): + if self.snippet_start_text in line: + if snippet_start_line != 0: + raise Exception( + f'Found multiple "{self.snippet_start_text}" lines in {script_relative_path}.\n' + f"The text with found on lines {snippet_start_line - snippet_start_line_offset + 1} and {idx + 1}." + ) + snippet_start_line = idx + 3 + if self.snippet_end_text in line: + if snippet_end_line != 0: + raise Exception( + f'Found multiple "{self.snippet_end_text}" lines in {script_relative_path}.\n' + f"The text with found on lines {snippet_end_line + snippet_end_line_offset + 1} and {idx + 1}." + ) + snippet_end_line = idx - snippet_end_line_offset + if snippet_start_line == 0: + raise Exception( + f'could not find the text "{self.snippet_start_text}" in {script_relative_path}' + ) + if snippet_end_line == 0: + raise Exception( + f'could not find the text "{self.snippet_end_text}" in {script_relative_path}' + ) + # return the fully constructed context return TestRunnerContext( + language=language, script_name=script_name, docker_pull=docker_pull, docker_run_test=docker_run_test, script_relative_path=script_relative_path, + script_name_with_file_type=script_name_with_file_type, script_output_file_path=script_output_file_path, script_output_file_name=script_output_file_name, prepared_file_path=prepared_file_path, + snippet_start_line=snippet_start_line, + snippet_end_line=snippet_end_line, ) class TestRunner: - # successful tracks the success status of the test runs - successful: None | bool = None - ctx: invoke.Context - - def __init__(self, ctx: invoke.Context) -> None: - self.ctx = ctx - - def run_tests(self, language, input_script): - # setup our baseline configuration - test_runner_context = TestRunnerContexts(language) - - # run every script - for ctx in test_runner_context.ctxs: + # __successful tracks the success status of the test runs + __successful: None | bool = None + invoke: invoke.Context + ctxs: TestRunnerContexts + + def __init__(self, _invoke, language) -> None: + self.invoke = _invoke + self.ctxs = TestRunnerContexts(language) + + def run_tests(self, input_script): + # run every test + for ctx in self.ctxs.ctxs: try: - # if an input script was passed in, and this script is not that input script - # then continue on to the next script + # determine if we this is one if the scripts we want to run if inputs_are_truthy_and_different( clean_string(input_script), clean_string(ctx.script_name), @@ -189,11 +234,11 @@ def run_tests(self, language, input_script): # We only do this in CI it helps with getting consistent timing in that context. # When running locally, you get consistent timing by running the script twice. if os.getenv("CI"): - self.ctx.run(ctx.docker_pull, echo=True, pty=True) + self.invoke.run(ctx.docker_pull, echo=True, pty=True) # run the script start_time = time.time() - output = self.ctx.run(ctx.docker_run_test, echo=True, pty=True) + output = self.invoke.run(ctx.docker_run_test, echo=True, pty=True) end_time = time.time() # report timing @@ -223,6 +268,7 @@ def run_tests(self, language, input_script): self.set_success_status(True) print(f'\tšŸŸ¢ script "{ctx.script_relative_path}" succeeded') + # check if the output file does not match the prepared file if os.path.exists(ctx.script_output_file_path) and not filecmp.cmp( ctx.prepared_file_path, ctx.script_output_file_path ): @@ -231,17 +277,80 @@ def run_tests(self, language, input_script): print( f"\t\t output file {ctx.script_output_file_name} has does not match the prepared file" ) + + # catch any errors, mark the test as failed, and continue + except Exception as exc: + print(self.error_context(ctx.data, exc)) + self.set_success_status(False) + + def generate_snippets(self, input_script): + # run every test + for ctx in self.ctxs.ctxs: + try: + # determine if we this is one if the scripts we want to run + if inputs_are_truthy_and_different( + clean_string(input_script), + clean_string(ctx.script_name), + ): + continue + + # read the snippet + os.makedirs(f"snippets/{ctx.language}", exist_ok=True) + with open( + ctx.script_relative_path, + "r", + encoding="utf-8", + ) as reader: + snippet = reader.readlines()[ + ctx.snippet_start_line : ctx.snippet_end_line + ] + + # write the snippet + with open( + f"snippets/{ctx.language}/{ctx.script_name_with_file_type}", + "w", + encoding="utf-8", + ) as writer: + writer.writelines(snippet) + + # check if there are unsaved changes on the snippet + self.invoke.run("git add snippets") + output = self.invoke.run( + f"git diff --cached --exit-code snippets/{ctx.language}/{ctx.script_name_with_file_type}", + warn=True, + ) + + # Check if there are unsaved changes on the snippets. + if output.exited != 0: + self.set_success_status(False) + print( + f"šŸ”“ snippets/{ctx.language}/{ctx.script_name_with_file_type} has uncommitted changes" + ) + + # catch any errors, mark the test as failed, and continue except Exception as exc: - print(json.dumps(ctx.data, indent=4)) - raise exc + print(self.error_context(ctx.data, exc)) + self.set_success_status(False) + + def error_context(self, data: dict, exc: Exception) -> str: + return "\n" + "\n".join( + [ + "TestRunnerContext:", + json.dumps(data, indent=4), + "".join(traceback.format_exception(None, exc, exc.__traceback__)), + ] + ) def set_success_status(self, status: bool): - # only update the test success status if it wasnt already false - if self.successful is not False: - self.successful = status + # Only update the test success status if it wasnt already false. + # This function is useless if the test has already failed. + # It's here to make sure you don't accidentally mark a test as successful + # when it has already failed. + if self.__successful is not False: + self.__successful = status def show_results(self): - if self.successful is True: + if self.__successful is True: print("\nāœØ script run success āœØ") sys.exit(0) else: @@ -253,8 +362,9 @@ def show_results(self): def test(ctx: invoke.Context, language, input_script): # language is the programming language to run scripts in # input_script is the name of a script you want to run - runner = TestRunner(ctx) - runner.run_tests(language, input_script) + runner = TestRunner(ctx, language) + runner.run_tests(input_script) + runner.generate_snippets(input_script) runner.show_results()