diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 0104402..4e9edde 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -38,7 +38,7 @@ ] } }, - "postCreateCommand": "cd ./completesolution/node; npm install; cd ../../; dotnet restore ./completesolution/dotnet/MinimalAPI.sln; dotnet restore ./exercisefiles/dotnet/MinimalAPI.sln" + "postCreateCommand": "sudo ./.devcontainer/scripts/install-cmake.sh; cd ./completesolution/node; npm install; cd ../../; dotnet restore ./completesolution/dotnet/MinimalAPI.sln; dotnet restore ./exercisefiles/dotnet/MinimalAPI.sln" // Features to add to the dev container. More info: https://containers.dev/features. // "features": {}, // Use 'forwardPorts' to make a list of ports inside the container available locally. diff --git a/.devcontainer/scripts/install-cmake.sh b/.devcontainer/scripts/install-cmake.sh new file mode 100755 index 0000000..be7cc95 --- /dev/null +++ b/.devcontainer/scripts/install-cmake.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +#------------------------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. +#------------------------------------------------------------------------------------------------------------- + +set -e + +echo "Installing apt packages..." +apt-get update +export DEBIAN_FRONTEND=noninteractive +apt-get -y install build-essential cmake cppcheck valgrind clang lldb llvm gdb +apt-get autoremove -y +apt-get clean -y +rm -rf /var/lib/apt/lists/* + +echo "Installing vcpkg..." +export VCPKG_ROOT=/usr/local/vcpkg +export VCPKG_DOWNLOADS=/usr/local/vcpkg-downloads +export PATH="${PATH}:${VCPKG_ROOT}" +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +"${DIR}/install-vcpkg.sh" vscode \ No newline at end of file diff --git a/.devcontainer/scripts/install-vcpkg.sh b/.devcontainer/scripts/install-vcpkg.sh new file mode 100755 index 0000000..3f62829 --- /dev/null +++ b/.devcontainer/scripts/install-vcpkg.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash +#------------------------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. +#------------------------------------------------------------------------------------------------------------- + +set -e + +USERNAME=${1:-"vscode"} + +. /etc/os-release + +# Add to bashrc/zshrc files for all users. +updaterc() { + echo "Updating /etc/bash.bashrc and /etc/zsh/zshrc..." + if [[ "$(cat /etc/bash.bashrc)" != *"$1"* ]]; then + echo -e "$1" >> /etc/bash.bashrc + fi + if [ -f "/etc/zsh/zshrc" ] && [[ "$(cat /etc/zsh/zshrc)" != *"$1"* ]]; then + echo -e "$1" >> /etc/zsh/zshrc + fi +} + +# Run apt-get if needed. +apt_get_update_if_needed() { + if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then + echo "Running apt-get update..." + apt-get update + else + echo "Skipping apt-get update." + fi +} + +# Check if packages are installed and installs them if not. +check_packages() { + if ! dpkg -s "$@" > /dev/null 2>&1; then + apt_get_update_if_needed + apt-get -y install --no-install-recommends "$@" + fi +} + +export DEBIAN_FRONTEND=noninteractive +export VCPKG_FORCE_SYSTEM_BINARIES=1 + +# Install additional packages needed by vcpkg: https://github.com/microsoft/vcpkg/blob/master/README.md#installing-linux-developer-tools +check_packages build-essential tar curl zip unzip pkg-config bash-completion ninja-build + +# Setup group and add user +umask 0002 +if ! cat /etc/group | grep -e "^vcpkg:" > /dev/null 2>&1; then + groupadd -r "vcpkg" +fi +usermod -a -G "vcpkg" "${USERNAME}" + +# Start Installation +# Clone repository with ports and installer +mkdir -p "${VCPKG_ROOT}" +mkdir -p "${VCPKG_DOWNLOADS}" +git clone --depth=1 \ + -c core.eol=lf \ + -c core.autocrlf=false \ + -c fsck.zeroPaddedFilemode=ignore \ + -c fetch.fsck.zeroPaddedFilemode=ignore \ + -c receive.fsck.zeroPaddedFilemode=ignore \ + https://github.com/microsoft/vcpkg "${VCPKG_ROOT}" + +## Run installer to get latest stable vcpkg binary +## https://github.com/microsoft/vcpkg/blob/7e7dad5fe20cdc085731343e0e197a7ae655555b/scripts/bootstrap.sh#L126-L144 +"${VCPKG_ROOT}"/bootstrap-vcpkg.sh + +# Add vcpkg to PATH +updaterc "$(cat << EOF +export VCPKG_ROOT="${VCPKG_ROOT}" +if [[ "\${PATH}" != *"\${VCPKG_ROOT}"* ]]; then export PATH="\${PATH}:\${VCPKG_ROOT}"; fi +EOF +)" + +# Give read/write permissions to the user group. +chown -R ":vcpkg" "${VCPKG_ROOT}" "${VCPKG_DOWNLOADS}" +chmod g+r+w+s "${VCPKG_ROOT}" "${VCPKG_DOWNLOADS}" +chmod -R g+r+w "${VCPKG_ROOT}" "${VCPKG_DOWNLOADS}" + +# Enable tab completion for bash and zsh +VCPKG_FORCE_SYSTEM_BINARIES=1 su "${USERNAME}" -c "${VCPKG_ROOT}/vcpkg integrate bash" +VCPKG_FORCE_SYSTEM_BINARIES=1 su "${USERNAME}" -c "${VCPKG_ROOT}/vcpkg integrate zsh" \ No newline at end of file diff --git a/.gitignore b/.gitignore index 78cae49..0aa96f3 100644 --- a/.gitignore +++ b/.gitignore @@ -530,3 +530,40 @@ FodyWeavers.xsd *.sln.iml .idea/ +# C++ excludes + +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +build + diff --git a/README.md b/README.md index e3e4e1a..129dbfd 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,9 @@ Run: **For Python** - [Install Python](https://www.python.org/downloads/) +**For C++** +- [Install cmake](https://cmake.org/download/) + ## Labs instructions - [Node Server](./exercisefiles/node/README.md) @@ -90,6 +93,7 @@ Run: - [Java Quarkus](./exercisefiles/quarkus/README.md) - [Python Data Engineer](./exercisefiles/dataengineer/README.md) - [Python Data Scientist](./exercisefiles/datascientist/README.md) +- [C++](./exercisefiles/c++/README.md) ## Challenges instructions diff --git a/completesolution/c++/CMakeLists.txt b/completesolution/c++/CMakeLists.txt new file mode 100644 index 0000000..958d99a --- /dev/null +++ b/completesolution/c++/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.14) + +project(OctoConverter + VERSION 1.0.0 + LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +enable_testing() + +add_subdirectory(src) +add_subdirectory(test) diff --git a/completesolution/c++/src/CMakeLists.txt b/completesolution/c++/src/CMakeLists.txt new file mode 100644 index 0000000..f25cc57 --- /dev/null +++ b/completesolution/c++/src/CMakeLists.txt @@ -0,0 +1,10 @@ +add_subdirectory(converters) +add_executable(main main.cpp) +target_link_libraries(main PRIVATE converters) + +set_target_properties( + main + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY + "${CMAKE_BINARY_DIR}" +) diff --git a/completesolution/c++/src/converters/CMakeLists.txt b/completesolution/c++/src/converters/CMakeLists.txt new file mode 100644 index 0000000..187e9fc --- /dev/null +++ b/completesolution/c++/src/converters/CMakeLists.txt @@ -0,0 +1,11 @@ +## Build a library "converters" with all included files +add_library( + converters + distance.cpp + distance.h + temperature.cpp + temperature.h + weight.cpp + weight.h + types.h +) diff --git a/completesolution/c++/src/converters/distance.cpp b/completesolution/c++/src/converters/distance.cpp new file mode 100644 index 0000000..073878f --- /dev/null +++ b/completesolution/c++/src/converters/distance.cpp @@ -0,0 +1,105 @@ +#include "distance.h" +#include +#include + +namespace /* private */ { + std::string getDistanceUnitSign(DistanceUnit unit) + { + std::unordered_map unitSigns = { + {DistanceUnit::Meters, "m"}, + {DistanceUnit::Feet, "ft"}, + {DistanceUnit::Yards, "yd"} + }; + + return unitSigns.at(unit); + } +} // namespace private + +namespace DistanceConversion +{ + void startFlow() + { + double sourceValue = getSourceValue(); + DistanceUnit from = getDistanceUnit("source"); + DistanceUnit to = getDistanceUnit("target"); + + double targetValue = convertDistance(sourceValue, from, to); + + std::cout << sourceValue << getDistanceUnitSign(from) << " is " << targetValue << getDistanceUnitSign(to) << std::endl; + } + + double getSourceValue() + { + double value; + std::cout << "Enter the value to be converted: "; + std::cin >> value; + return value; + } + + DistanceUnit getDistanceUnit(const std::string_view &sourceOrTarget) + { + int choice; + std::cout << "Select " << sourceOrTarget << " distance unit:\n"; + std::cout << "[1] Meters\n"; + std::cout << "[2] Feet\n"; + std::cout << "[3] Yards\n"; + std::cout << "Enter choice: "; + std::cin >> choice; + + switch (choice) + { + case 1: + return DistanceUnit::Meters; + case 2: + return DistanceUnit::Feet; + case 3: + return DistanceUnit::Yards; + default: + return DistanceUnit::Meters; // Default to Meters + } + } + + double convertDistance(double value, DistanceUnit from, DistanceUnit to) + { + if (from == to) + { + return value; + } + + if (from == DistanceUnit::Meters) + { + if (to == DistanceUnit::Feet) + { + return value * 3.28084; + } + else if (to == DistanceUnit::Yards) + { + return value * 1.09361; + } + } + else if (from == DistanceUnit::Feet) + { + if (to == DistanceUnit::Meters) + { + return value / 3.28084; + } + else if (to == DistanceUnit::Yards) + { + return value / 3.0; + } + } + else if (from == DistanceUnit::Yards) + { + if (to == DistanceUnit::Meters) + { + return value / 1.09361; + } + else if (to == DistanceUnit::Feet) + { + return value * 3.0; + } + } + + return 0; + } +} \ No newline at end of file diff --git a/completesolution/c++/src/converters/distance.h b/completesolution/c++/src/converters/distance.h new file mode 100644 index 0000000..9068518 --- /dev/null +++ b/completesolution/c++/src/converters/distance.h @@ -0,0 +1,21 @@ +#ifndef DISTANCE_H +#define DISTANCE_H + +#include + +enum class DistanceUnit +{ + Meters, + Feet, + Yards +}; + +namespace DistanceConversion +{ + void startFlow(); + double getSourceValue(); + DistanceUnit getDistanceUnit(const std::string_view &sourceOrTarget); + double convertDistance(double value, DistanceUnit from, DistanceUnit to); +} + +#endif diff --git a/completesolution/c++/src/converters/temperature.cpp b/completesolution/c++/src/converters/temperature.cpp new file mode 100644 index 0000000..013163a --- /dev/null +++ b/completesolution/c++/src/converters/temperature.cpp @@ -0,0 +1,92 @@ +#include "temperature.h" +#include +#include + +namespace /* private */ +{ + std::string getTemperatureUnitSign(TemperatureUnit unit) + { + std::unordered_map unitSigns = { + {TemperatureUnit::Celsius, "°C"}, + {TemperatureUnit::Fahrenheit, "°F"}, + {TemperatureUnit::Kelvin, "K"} + }; + + return unitSigns.at(unit); + } +} // namespace private + +namespace TemperatureConversion +{ + void startFlow() + { + double sourceValue = getSourceValue(); + TemperatureUnit from = getTemperatureUnit("source"); + TemperatureUnit to = getTemperatureUnit("target"); + + double targetValue = convertTemperature(sourceValue, from, to); + + std::cout << sourceValue << getTemperatureUnitSign(from) << " is " << targetValue << getTemperatureUnitSign(to) << std::endl; + } + + double getSourceValue() + { + double value; + std::cout << "Enter the value to be converted: "; + std::cin >> value; + return value; + } + + TemperatureUnit getTemperatureUnit(const std::string_view &sourceOrTarget) + { + int choice; + std::cout << "Select " << sourceOrTarget << " temperature unit:\n"; + std::cout << "[1] Celsius\n"; + std::cout << "[2] Fahrenheit\n"; + std::cout << "[3] Kelvin\n"; + std::cout << "Enter choice: "; + std::cin >> choice; + + switch (choice) + { + case 1: + return TemperatureUnit::Celsius; + case 2: + return TemperatureUnit::Fahrenheit; + case 3: + return TemperatureUnit::Kelvin; + default: + return TemperatureUnit::Celsius; // Default to Celsius + } + } + + double convertTemperature(double value, TemperatureUnit from, TemperatureUnit to) + { + if (from == to) + { + return value; + } + + // Convert everything to Celsius first + if (from == TemperatureUnit::Fahrenheit) + { + value = (value - 32) * 5 / 9; + } + else if (from == TemperatureUnit::Kelvin) + { + value -= 273.15; + } + + // Then convert to the target unit + if (to == TemperatureUnit::Fahrenheit) + { + value = value * 9 / 5 + 32; + } + else if (to == TemperatureUnit::Kelvin) + { + value += 273.15; + } + + return value; + } +} \ No newline at end of file diff --git a/completesolution/c++/src/converters/temperature.h b/completesolution/c++/src/converters/temperature.h new file mode 100644 index 0000000..94b8c12 --- /dev/null +++ b/completesolution/c++/src/converters/temperature.h @@ -0,0 +1,22 @@ +#ifndef TEMPERATURE_H +#define TEMPERATURE_H + +#include "types.h" +#include + +enum class TemperatureUnit +{ + Celsius, + Fahrenheit, + Kelvin +}; + +namespace TemperatureConversion +{ + void startFlow(); + double getSourceValue(); + TemperatureUnit getTemperatureUnit(const std::string_view &sourceOrTarget); + double convertTemperature(double value, TemperatureUnit from, TemperatureUnit to); +} + +#endif diff --git a/completesolution/c++/src/converters/types.h b/completesolution/c++/src/converters/types.h new file mode 100644 index 0000000..6f4752f --- /dev/null +++ b/completesolution/c++/src/converters/types.h @@ -0,0 +1,11 @@ +#ifndef TYPES +#define TYPES + +enum class ConversionType +{ + Temperature, + Distance, + Weight, +}; + +#endif diff --git a/completesolution/c++/src/converters/weight.cpp b/completesolution/c++/src/converters/weight.cpp new file mode 100644 index 0000000..fb007b1 --- /dev/null +++ b/completesolution/c++/src/converters/weight.cpp @@ -0,0 +1,92 @@ +#include "weight.h" +#include +#include + +namespace /* private */ +{ + std::string getWeightUnitSign(WeightConversion::WeightUnit unit) + { + std::unordered_map unitSigns = { + {WeightConversion::WeightUnit::Kilograms, "kg"}, + {WeightConversion::WeightUnit::Pounds, "lb"}, + {WeightConversion::WeightUnit::Ounces, "oz"} + }; + + return unitSigns.at(unit); + } +} // namespace private + +namespace WeightConversion +{ + void startFlow() + { + double sourceValue = getSourceValue(); + WeightUnit from = getWeightUnit("source"); + WeightUnit to = getWeightUnit("target"); + + double targetValue = convertWeight(sourceValue, from, to); + + std::cout << sourceValue << getWeightUnitSign(from) << " is " << targetValue << getWeightUnitSign(to) << std::endl; + } + + double getSourceValue() + { + double value; + std::cout << "Enter the value to be converted: "; + std::cin >> value; + return value; + } + + WeightUnit getWeightUnit(const std::string_view &sourceOrTarget) + { + int choice; + std::cout << "Select " << sourceOrTarget << " weight unit:\n"; + std::cout << "[1] Kilograms\n"; + std::cout << "[2] Pounds\n"; + std::cout << "[3] Ounces\n"; + std::cout << "Enter choice: "; + std::cin >> choice; + + switch (choice) + { + case 1: + return WeightUnit::Kilograms; + case 2: + return WeightUnit::Pounds; + case 3: + return WeightUnit::Ounces; + default: + return WeightUnit::Kilograms; // Default to Kilograms + } + } + + double convertWeight(double value, WeightUnit from, WeightUnit to) + { + if (from == to) + { + return value; + } + + // Convert everything to Kilograms first + if (from == WeightUnit::Pounds) + { + value = value * 0.453592; + } + else if (from == WeightUnit::Ounces) + { + value = value * 0.0283495; + } + + // Then convert to the target unit + if (to == WeightUnit::Pounds) + { + value = value / 0.453592; + } + else if (to == WeightUnit::Ounces) + { + value = value / 0.0283495; + } + + return value; + } +} \ No newline at end of file diff --git a/completesolution/c++/src/converters/weight.h b/completesolution/c++/src/converters/weight.h new file mode 100644 index 0000000..7dadc58 --- /dev/null +++ b/completesolution/c++/src/converters/weight.h @@ -0,0 +1,21 @@ +#include + +#ifndef WEIGHT_H +#define WEIGHT_H + +namespace WeightConversion +{ + enum class WeightUnit + { + Kilograms, + Pounds, + Ounces + }; + + void startFlow(); + double getSourceValue(); + WeightUnit getWeightUnit(const std::string_view &sourceOrTarget); + double convertWeight(double value, WeightUnit from, WeightUnit to); +} + +#endif // WEIGHT_H \ No newline at end of file diff --git a/completesolution/c++/src/main.cpp b/completesolution/c++/src/main.cpp new file mode 100644 index 0000000..030a77b --- /dev/null +++ b/completesolution/c++/src/main.cpp @@ -0,0 +1,55 @@ +#include "converters/types.h" +#include "converters/temperature.h" +#include "converters/distance.h" +#include "converters/weight.h" +#include +#include + +ConversionType getConversionType() +{ + int choice; + std::array conversionTypes = { + ConversionType::Temperature, + ConversionType::Distance, + ConversionType::Weight}; + + while (true) + { + std::cout << "Select type of conversion:\n"; + std::cout << "[1] Temperature\n"; + std::cout << "[2] Distance\n"; + std::cout << "[3] Weight\n"; + std::cout << "Enter choice: "; + std::cin >> choice; + + if (choice > 0 && choice <= conversionTypes.size()) + { + return conversionTypes[choice - 1]; + } + + std::cout << "Invalid choice. Please try again.\n"; + } +} + +int main() +{ + ConversionType type = getConversionType(); + switch (type) + { + case ConversionType::Temperature: + { + TemperatureConversion::startFlow(); + break; + } + case ConversionType::Distance: + { + DistanceConversion::startFlow(); + break; + } + case ConversionType::Weight: + { + WeightConversion::startFlow(); + break; + } + } +} \ No newline at end of file diff --git a/completesolution/c++/test/CMakeLists.txt b/completesolution/c++/test/CMakeLists.txt new file mode 100644 index 0000000..889ec27 --- /dev/null +++ b/completesolution/c++/test/CMakeLists.txt @@ -0,0 +1,38 @@ +set(target run-tests) + +add_executable( + ${target} + temperature_test.cpp + distance_test.cpp + weight_test.cpp +) + +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip +) +# For Windows: Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) + +target_link_libraries(${target} GTest::gtest_main) + +target_include_directories( + ${target} + PUBLIC "${CMAKE_SOURCE_DIR}/src/converters" +) + +target_link_libraries(${target} converters) + +add_test(NAME ${target} COMMAND ${target}) + +include(GoogleTest) +gtest_discover_tests(${target}) + +set_target_properties( + ${target} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY + "${CMAKE_BINARY_DIR}" +) diff --git a/completesolution/c++/test/distance_test.cpp b/completesolution/c++/test/distance_test.cpp new file mode 100644 index 0000000..44e0d09 --- /dev/null +++ b/completesolution/c++/test/distance_test.cpp @@ -0,0 +1,32 @@ +#include "gtest/gtest.h" +#include "distance.h" + +TEST(DistanceConversionTest, MetersToFeet) { + double result = DistanceConversion::convertDistance(1.0, DistanceUnit::Meters, DistanceUnit::Feet); + ASSERT_NEAR(result, 3.28084, 0.00001); +} + +TEST(DistanceConversionTest, FeetToMeters) { + double result = DistanceConversion::convertDistance(3.28084, DistanceUnit::Feet, DistanceUnit::Meters); + ASSERT_NEAR(result, 1.0, 0.00001); +} + +TEST(DistanceConversionTest, MetersToYards) { + double result = DistanceConversion::convertDistance(1.0, DistanceUnit::Meters, DistanceUnit::Yards); + ASSERT_NEAR(result, 1.09361, 0.00001); +} + +TEST(DistanceConversionTest, YardsToMeters) { + double result = DistanceConversion::convertDistance(1.09361, DistanceUnit::Yards, DistanceUnit::Meters); + ASSERT_NEAR(result, 1.0, 0.00001); +} + +TEST(DistanceConversionTest, FeetToYards) { + double result = DistanceConversion::convertDistance(3.0, DistanceUnit::Feet, DistanceUnit::Yards); + ASSERT_NEAR(result, 1.0, 0.00001); +} + +TEST(DistanceConversionTest, YardsToFeet) { + double result = DistanceConversion::convertDistance(1.0, DistanceUnit::Yards, DistanceUnit::Feet); + ASSERT_NEAR(result, 3.0, 0.00001); +} \ No newline at end of file diff --git a/completesolution/c++/test/temperature_test.cpp b/completesolution/c++/test/temperature_test.cpp new file mode 100644 index 0000000..381b7c8 --- /dev/null +++ b/completesolution/c++/test/temperature_test.cpp @@ -0,0 +1,32 @@ +#include "temperature.h" +#include + +TEST(TemperatureConverterTest, FahrenheitToCelsius) { + double result = TemperatureConversion::convertTemperature(32, TemperatureUnit::Fahrenheit, TemperatureUnit::Celsius); + ASSERT_DOUBLE_EQ(result, 0); +} + +TEST(TemperatureConverterTest, KelvinToCelsius) { + double result = TemperatureConversion::convertTemperature(273.15, TemperatureUnit::Kelvin, TemperatureUnit::Celsius); + ASSERT_DOUBLE_EQ(result, 0); +} + +TEST(TemperatureConverterTest, CelsiusToFahrenheit) { + double result = TemperatureConversion::convertTemperature(0, TemperatureUnit::Celsius, TemperatureUnit::Fahrenheit); + ASSERT_DOUBLE_EQ(result, 32); +} + +TEST(TemperatureConverterTest, CelsiusToKelvin) { + double result = TemperatureConversion::convertTemperature(0, TemperatureUnit::Celsius, TemperatureUnit::Kelvin); + ASSERT_DOUBLE_EQ(result, 273.15); +} + +TEST(TemperatureConverterTest, FahrenheitToKelvin) { + double result = TemperatureConversion::convertTemperature(32, TemperatureUnit::Fahrenheit, TemperatureUnit::Kelvin); + ASSERT_DOUBLE_EQ(result, 273.15); +} + +TEST(TemperatureConverterTest, KelvinToFahrenheit) { + double result = TemperatureConversion::convertTemperature(273.15, TemperatureUnit::Kelvin, TemperatureUnit::Fahrenheit); + ASSERT_DOUBLE_EQ(result, 32); +} diff --git a/completesolution/c++/test/weight_test.cpp b/completesolution/c++/test/weight_test.cpp new file mode 100644 index 0000000..a0b483a --- /dev/null +++ b/completesolution/c++/test/weight_test.cpp @@ -0,0 +1,32 @@ +#include +#include "weight.h" + +TEST(WeightConversionTest, KilogramsToPounds) { + double result = WeightConversion::convertWeight(1.0, WeightConversion::WeightUnit::Kilograms, WeightConversion::WeightUnit::Pounds); + ASSERT_NEAR(result, 2.20462, 0.00001); +} + +TEST(WeightConversionTest, PoundsToKilograms) { + double result = WeightConversion::convertWeight(2.20462, WeightConversion::WeightUnit::Pounds, WeightConversion::WeightUnit::Kilograms); + ASSERT_NEAR(result, 1.0, 0.00001); +} + +TEST(WeightConversionTest, KilogramsToOunces) { + double result = WeightConversion::convertWeight(1.0, WeightConversion::WeightUnit::Kilograms, WeightConversion::WeightUnit::Ounces); + ASSERT_NEAR(result, 35.274, 0.001); +} + +TEST(WeightConversionTest, OuncesToKilograms) { + double result = WeightConversion::convertWeight(35.274, WeightConversion::WeightUnit::Ounces, WeightConversion::WeightUnit::Kilograms); + ASSERT_NEAR(result, 1.0, 0.001); +} + +TEST(WeightConversionTest, PoundsToOunces) { + double result = WeightConversion::convertWeight(1.0, WeightConversion::WeightUnit::Pounds, WeightConversion::WeightUnit::Ounces); + ASSERT_NEAR(result, 16.0, 0.00001); +} + +TEST(WeightConversionTest, OuncesToPounds) { + double result = WeightConversion::convertWeight(16.0, WeightConversion::WeightUnit::Ounces, WeightConversion::WeightUnit::Pounds); + ASSERT_NEAR(result, 1.0, 0.00001); +} \ No newline at end of file diff --git a/exercisefiles/c++/CMakeLists.txt b/exercisefiles/c++/CMakeLists.txt new file mode 100644 index 0000000..958d99a --- /dev/null +++ b/exercisefiles/c++/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.14) + +project(OctoConverter + VERSION 1.0.0 + LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +enable_testing() + +add_subdirectory(src) +add_subdirectory(test) diff --git a/exercisefiles/c++/README.md b/exercisefiles/c++/README.md new file mode 100644 index 0000000..45dbefd --- /dev/null +++ b/exercisefiles/c++/README.md @@ -0,0 +1,37 @@ +# C++ Demo + +These exercises demonstrate usage of GitHub Copilot for C++. The app is a simple CLI Tool that allows users to convert in between different units. + +## Getting Started + +### Build + +`cd` to the `exercisefiles/c++` directory. Run the following two commands to prepare the make file and then build the project: + +```bash +cmake -S . -B build +cmake --build build +``` + +### Run & Test + +- To run the main program: + + ```bash + ./build/main + ``` + +- To execute the tests: + + ```bash + ./build/run-tests + ``` + +## Exercises + +Use GitHub Copilot to assist with the following tasks: + +- [ ] Finish the `Distance` Conversion Class and include it in the `main.cpp` (optionally, first implement tests and do a TDD approach) +- [ ] Add some Tests for the `Distance::convertDistance` +- [ ] Refactor all `printf` and `scanf` and use `std::cout` and `std::cin` instead +- [ ] Add a new Conversion Class for `Weight` to convert in between Kilos and Pounds \ No newline at end of file diff --git a/exercisefiles/c++/src/CMakeLists.txt b/exercisefiles/c++/src/CMakeLists.txt new file mode 100644 index 0000000..f25cc57 --- /dev/null +++ b/exercisefiles/c++/src/CMakeLists.txt @@ -0,0 +1,10 @@ +add_subdirectory(converters) +add_executable(main main.cpp) +target_link_libraries(main PRIVATE converters) + +set_target_properties( + main + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY + "${CMAKE_BINARY_DIR}" +) diff --git a/exercisefiles/c++/src/converters/CMakeLists.txt b/exercisefiles/c++/src/converters/CMakeLists.txt new file mode 100644 index 0000000..e271c0e --- /dev/null +++ b/exercisefiles/c++/src/converters/CMakeLists.txt @@ -0,0 +1,9 @@ +## Build a library "converters" with all included files +add_library( + converters + distance.cpp + distance.h + temperature.cpp + temperature.h + types.h +) diff --git a/exercisefiles/c++/src/converters/distance.cpp b/exercisefiles/c++/src/converters/distance.cpp new file mode 100644 index 0000000..debcd7c --- /dev/null +++ b/exercisefiles/c++/src/converters/distance.cpp @@ -0,0 +1,67 @@ +#include "distance.h" +#include +#include + +namespace /* private */ { + std::string getDistanceUnitSign(DistanceUnit unit) + { + std::unordered_map unitSigns = { + {DistanceUnit::Meters, "m"}, + {DistanceUnit::Feet, "ft"}, + {DistanceUnit::Yards, "yd"} + }; + + return unitSigns.at(unit); + } +} // namespace private + +namespace DistanceConversion +{ + void startFlow() + { + double sourceValue = getSourceValue(); + DistanceUnit from = getDistanceUnit("source"); + DistanceUnit to = getDistanceUnit("target"); + + double targetValue = convertDistance(sourceValue, from, to); + + printf("%.2f%s is %.2f%s\n", sourceValue, getDistanceUnitSign(from).c_str(), targetValue, getDistanceUnitSign(to).c_str()); + } + + double getSourceValue() + { + double value; + printf("Enter the value to be converted: "); + scanf("%lf", &value); + return value; + } + + DistanceUnit getDistanceUnit(const std::string_view &sourceOrTarget) + { + int choice; + printf("Select %s distance unit:\n", std::string(sourceOrTarget).c_str()); + printf("[1] Meters\n"); + printf("[2] Feet\n"); + printf("[3] Yards\n"); + printf("Enter choice: "); + scanf("%d", &choice); + + switch (choice) + { + case 1: + return DistanceUnit::Meters; + case 2: + return DistanceUnit::Feet; + case 3: + return DistanceUnit::Yards; + default: + return DistanceUnit::Meters; // Default to Meters + } + } + + double convertDistance(double value, DistanceUnit from, DistanceUnit to) + { + // Todo implement the actual conversion logic + return 0; + } +} diff --git a/exercisefiles/c++/src/converters/distance.h b/exercisefiles/c++/src/converters/distance.h new file mode 100644 index 0000000..9068518 --- /dev/null +++ b/exercisefiles/c++/src/converters/distance.h @@ -0,0 +1,21 @@ +#ifndef DISTANCE_H +#define DISTANCE_H + +#include + +enum class DistanceUnit +{ + Meters, + Feet, + Yards +}; + +namespace DistanceConversion +{ + void startFlow(); + double getSourceValue(); + DistanceUnit getDistanceUnit(const std::string_view &sourceOrTarget); + double convertDistance(double value, DistanceUnit from, DistanceUnit to); +} + +#endif diff --git a/exercisefiles/c++/src/converters/temperature.cpp b/exercisefiles/c++/src/converters/temperature.cpp new file mode 100644 index 0000000..fec16ba --- /dev/null +++ b/exercisefiles/c++/src/converters/temperature.cpp @@ -0,0 +1,92 @@ +#include "temperature.h" +#include +#include +namespace /* private */ +{ + + std::string getTemperatureUnitSign(TemperatureUnit unit) + { + std::unordered_map unitSigns = { + {TemperatureUnit::Celsius, "°C"}, + {TemperatureUnit::Fahrenheit, "°F"}, + {TemperatureUnit::Kelvin, "K"} + }; + + return unitSigns.at(unit); + } +} // namespace private + +namespace TemperatureConversion +{ + void startFlow() + { + double sourceValue = getSourceValue(); + TemperatureUnit from = getTemperatureUnit("source"); + TemperatureUnit to = getTemperatureUnit("target"); + + double targetValue = convertTemperature(sourceValue, from, to); + + printf("%.2f%s is %.2f%s\n", sourceValue, getTemperatureUnitSign(from).c_str(), targetValue, getTemperatureUnitSign(to).c_str()); + } + + double getSourceValue() + { + double value; + printf("Enter the value to be converted: "); + scanf("%lf", &value); + return value; + } + + TemperatureUnit getTemperatureUnit(const std::string_view &sourceOrTarget) + { + int choice; + printf("Select %s temperature unit:\n", std::string(sourceOrTarget).c_str()); + printf("[1] Celsius\n"); + printf("[2] Fahrenheit\n"); + printf("[3] Kelvin\n"); + printf("Enter choice: "); + scanf("%d", &choice); + + switch (choice) + { + case 1: + return TemperatureUnit::Celsius; + case 2: + return TemperatureUnit::Fahrenheit; + case 3: + return TemperatureUnit::Kelvin; + default: + return TemperatureUnit::Celsius; + } + } + + double convertTemperature(double value, TemperatureUnit from, TemperatureUnit to) + { + if (from == to) + { + return value; + } + + // Convert everything to Celsius first + if (from == TemperatureUnit::Fahrenheit) + { + value = (value - 32) * 5 / 9; + } + else if (from == TemperatureUnit::Kelvin) + { + value -= 273.15; + } + + // Then convert to the target unit + if (to == TemperatureUnit::Fahrenheit) + { + value = value * 9 / 5 + 32; + } + else if (to == TemperatureUnit::Kelvin) + { + value += 273.15; + } + + return value; + } +} diff --git a/exercisefiles/c++/src/converters/temperature.h b/exercisefiles/c++/src/converters/temperature.h new file mode 100644 index 0000000..94b8c12 --- /dev/null +++ b/exercisefiles/c++/src/converters/temperature.h @@ -0,0 +1,22 @@ +#ifndef TEMPERATURE_H +#define TEMPERATURE_H + +#include "types.h" +#include + +enum class TemperatureUnit +{ + Celsius, + Fahrenheit, + Kelvin +}; + +namespace TemperatureConversion +{ + void startFlow(); + double getSourceValue(); + TemperatureUnit getTemperatureUnit(const std::string_view &sourceOrTarget); + double convertTemperature(double value, TemperatureUnit from, TemperatureUnit to); +} + +#endif diff --git a/exercisefiles/c++/src/converters/types.h b/exercisefiles/c++/src/converters/types.h new file mode 100644 index 0000000..80e333c --- /dev/null +++ b/exercisefiles/c++/src/converters/types.h @@ -0,0 +1,10 @@ +#ifndef TYPES +#define TYPES + +enum class ConversionType +{ + Temperature, + Distance +}; + +#endif diff --git a/exercisefiles/c++/src/main.cpp b/exercisefiles/c++/src/main.cpp new file mode 100644 index 0000000..7bfba3d --- /dev/null +++ b/exercisefiles/c++/src/main.cpp @@ -0,0 +1,40 @@ +#include "converters/types.h" +#include "converters/temperature.h" +#include +#include + +ConversionType getConversionType() +{ + int choice; + std::array conversionTypes = { + ConversionType::Temperature, + ConversionType::Distance}; + + while (true) + { + printf("Select type of conversion:\n"); + printf("[1] Temperature\n"); + printf("Enter choice: "); + scanf("%d", &choice); + + if (choice > 0 && choice <= conversionTypes.size()) + { + return conversionTypes[choice - 1]; + } + + printf("Invalid choice. Please try again.\n"); + } +} + +int main() +{ + ConversionType type = getConversionType(); + switch (type) + { + case ConversionType::Temperature: + { + TemperatureConversion::startFlow(); + break; + } + } +} diff --git a/exercisefiles/c++/test/CMakeLists.txt b/exercisefiles/c++/test/CMakeLists.txt new file mode 100644 index 0000000..616ef8f --- /dev/null +++ b/exercisefiles/c++/test/CMakeLists.txt @@ -0,0 +1,36 @@ +set(target run-tests) + +add_executable( + ${target} + temperature_test.cpp +) + +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip +) +# For Windows: Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) + +target_link_libraries(${target} GTest::gtest_main) + +target_include_directories( + ${target} + PUBLIC "${CMAKE_SOURCE_DIR}/src/converters" +) + +target_link_libraries(${target} converters) + +add_test(NAME ${target} COMMAND ${target}) + +include(GoogleTest) +gtest_discover_tests(${target}) + +set_target_properties( + ${target} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY + "${CMAKE_BINARY_DIR}" +) diff --git a/exercisefiles/c++/test/temperature_test.cpp b/exercisefiles/c++/test/temperature_test.cpp new file mode 100644 index 0000000..381b7c8 --- /dev/null +++ b/exercisefiles/c++/test/temperature_test.cpp @@ -0,0 +1,32 @@ +#include "temperature.h" +#include + +TEST(TemperatureConverterTest, FahrenheitToCelsius) { + double result = TemperatureConversion::convertTemperature(32, TemperatureUnit::Fahrenheit, TemperatureUnit::Celsius); + ASSERT_DOUBLE_EQ(result, 0); +} + +TEST(TemperatureConverterTest, KelvinToCelsius) { + double result = TemperatureConversion::convertTemperature(273.15, TemperatureUnit::Kelvin, TemperatureUnit::Celsius); + ASSERT_DOUBLE_EQ(result, 0); +} + +TEST(TemperatureConverterTest, CelsiusToFahrenheit) { + double result = TemperatureConversion::convertTemperature(0, TemperatureUnit::Celsius, TemperatureUnit::Fahrenheit); + ASSERT_DOUBLE_EQ(result, 32); +} + +TEST(TemperatureConverterTest, CelsiusToKelvin) { + double result = TemperatureConversion::convertTemperature(0, TemperatureUnit::Celsius, TemperatureUnit::Kelvin); + ASSERT_DOUBLE_EQ(result, 273.15); +} + +TEST(TemperatureConverterTest, FahrenheitToKelvin) { + double result = TemperatureConversion::convertTemperature(32, TemperatureUnit::Fahrenheit, TemperatureUnit::Kelvin); + ASSERT_DOUBLE_EQ(result, 273.15); +} + +TEST(TemperatureConverterTest, KelvinToFahrenheit) { + double result = TemperatureConversion::convertTemperature(273.15, TemperatureUnit::Kelvin, TemperatureUnit::Fahrenheit); + ASSERT_DOUBLE_EQ(result, 32); +} diff --git a/exercisefiles/springboot/README.md b/exercisefiles/springboot/README.md index afc79e9..46bfe16 100644 --- a/exercisefiles/springboot/README.md +++ b/exercisefiles/springboot/README.md @@ -12,9 +12,13 @@ Let's start copiloting!!! ### 1. Create the code to handle a simple GET request -Move to the 'DemoController.java' file and start writing the code to handle a simple GET request. In this first exercise, we have provided a comment that describes the code you need to generate. Just press enter and wait a couple of seconds, Copilot will generate the code for you. +Move to the [DemoController.java](exercisefiles/springboot/copilot-demo/src/main/java/com/microsoft/hackathon/copilotdemo/controller/DemoController.java) file and start writing the code to handle a simple GET request. In this first exercise, we have provided a comment that describes the code you need to generate. Just press enter and wait a couple of seconds, Copilot will generate the code for you. -There is already a unit test implemented for this exercise, you can run it using the command `mvn test` before and after to validate that the code generated by Copilot is correct. +> Tip: If nothing happens, then change the comment (add a word or 2) - sometimes Copilot needs a little nudge. + +There is already a unit test implemented for this exercise. Open the terminal in the Codespace. `cd` to the correct folder by entering this command: `cd exercisefiles/springboot/copilot-demo`. Now, you can run the test by entering the command `mvn test` before and after you make changes to validate that the code generated by Copilot is correct. + +> Tip: If the tests fail and you get a `404 not found`, try open the [DemoController.java](exercisefiles/springboot/copilot-demo/src/main/java/com/microsoft/hackathon/copilotdemo/controller/DemoController.java) file and the [CopilotDemoApplicationTests.java](exercisefiles/springboot/copilot-demo/src/test/java/com/microsoft/hackathon/copilotdemo/CopilotDemoApplicationTests.java) files. Then open Copilot Chat and ask Copilot: "Why is this the hello test failing Then, create a new unit test for the case when no key is provided in the request.