From 86a76f133e8a8d38724a1c6e9c31d49cb7366bde Mon Sep 17 00:00:00 2001 From: "Gustavo A. Bastian" Date: Thu, 28 Nov 2024 16:11:26 -0300 Subject: [PATCH 1/7] feat: adding locale to medicine Module (#1002) * feat: adding locale to medicine module, add es_AR and pt_BR language definitions and use es_AR definitions for all es locales. * fix: formatting * fix: formatting * fix: formatting * formatting * fix: reformatting * fix: another formatting * fix: check formatting * fix: last line in medicine_data.h * fix: using es_AR definitions for all countries with es as language * fix: adding new line at end medicine_data.h --- include/faker-cxx/medicine.h | 10 +- src/modules/medicine.cpp | 28 ++- src/modules/medicine_data.h | 300 +++++++++++++++++++++++++++++++- tests/modules/medicine_test.cpp | 54 ++++-- 4 files changed, 369 insertions(+), 23 deletions(-) diff --git a/include/faker-cxx/medicine.h b/include/faker-cxx/medicine.h index 3929074fb..3cacda320 100644 --- a/include/faker-cxx/medicine.h +++ b/include/faker-cxx/medicine.h @@ -3,11 +3,13 @@ #include #include "faker-cxx/export.h" +#include "faker-cxx/types/locale.h" namespace faker::medicine { /** * @brief Returns a random medical condition. + * @param locale The locale. Defaults to `Locale::en_US`. * * @returns Medical condition. * @@ -15,10 +17,11 @@ namespace faker::medicine * faker::medicine::condition() // "AIDS" * @endcode */ -FAKER_CXX_EXPORT std::string_view condition(); +FAKER_CXX_EXPORT std::string_view condition(Locale locale = Locale::en_US); /** * @brief Returns a random medical test + * @param locale The locale. Defaults to `Locale::en_US`. * * @returns Medical test. * @@ -26,10 +29,11 @@ FAKER_CXX_EXPORT std::string_view condition(); * faker::medicine::medicalTest() // "pulmonary auscultation" * @endcode */ -FAKER_CXX_EXPORT std::string_view medicalTest(); +FAKER_CXX_EXPORT std::string_view medicalTest(Locale locale = Locale::en_US); /** * @brief Returns a random Medical specialty + * @param locale The locale. Defaults to `Locale::en_US`. * * @returns Medical specialty. * @@ -38,5 +42,5 @@ FAKER_CXX_EXPORT std::string_view medicalTest(); * @endcode */ -FAKER_CXX_EXPORT std::string_view specialty(); +FAKER_CXX_EXPORT std::string_view specialty(Locale locale = Locale::en_US); } diff --git a/src/modules/medicine.cpp b/src/modules/medicine.cpp index c20f83d2d..057c3ce48 100644 --- a/src/modules/medicine.cpp +++ b/src/modules/medicine.cpp @@ -7,19 +7,35 @@ namespace faker::medicine { -std::string_view condition() + +std::string_view condition(Locale locale) { - return helper::randomElement(medicalConditions); + auto localeLocal = locale; + if (medicineMapSpan.find(locale) == medicineMapSpan.end()) + { + localeLocal = Locale::en_US; + } + return helper::randomElement((medicineMapSpan.at(localeLocal)).medicalConditions); } -std::string_view medicalTest() +std::string_view medicalTest(Locale locale) { - return helper::randomElement(medicalTests); + auto localeLocal = locale; + if (medicineMapSpan.find(locale) == medicineMapSpan.end()) + { + localeLocal = Locale::en_US; + } + return helper::randomElement((medicineMapSpan.at(localeLocal)).medicalTests); } -std::string_view specialty() +std::string_view specialty(Locale locale) { - return helper::randomElement(specialties); + auto localeLocal = locale; + if (medicineMapSpan.find(locale) == medicineMapSpan.end()) + { + localeLocal = Locale::en_US; + } + return helper::randomElement((medicineMapSpan.at(localeLocal)).specialties); } } diff --git a/src/modules/medicine_data.h b/src/modules/medicine_data.h index 3d9b4cfb6..5d302f36e 100644 --- a/src/modules/medicine_data.h +++ b/src/modules/medicine_data.h @@ -1,11 +1,21 @@ #pragma once #include +#include +#include #include namespace faker::medicine { -const auto medicalConditions = std::to_array({ +struct MedicineDefinition +{ + std::span medicalConditions; + std::span medicalTests; + std::span specialties; +}; + +//"en_US" +const auto enUSMedicalConditions = std::to_array({ "AIDS", "Anorexia Nervosa", "Arthritis Juvenile Rheumatoid", @@ -67,7 +77,7 @@ const auto medicalConditions = std::to_array({ "Ulcers Stomach or Intestinal", }); -const auto medicalTests = std::to_array({ +const auto enUSMedicalTests = std::to_array({ "HEENT examination", "abdominal palpation", "amniocentesis", @@ -105,7 +115,7 @@ const auto medicalTests = std::to_array({ "vaginal examination", }); -const auto specialties = std::to_array({ +const auto enUSSpecialties = std::to_array({ "Accident and emergency medicine", "Allergist", "Anaesthetics", @@ -162,4 +172,288 @@ const auto specialties = std::to_array({ "Vascular surgery", "Venereology", }); + +const MedicineDefinition enUSMedicineDefinition = { + .medicalConditions = enUSMedicalConditions, .medicalTests = enUSMedicalTests, .specialties = enUSSpecialties}; + +//"es_AR" +const auto esARMedicalConditions = std::to_array({ + "SIDA", + "Anorexia nerviosa", + "Artritis Reumatoide Juvenil", + "Asma Moderada o Grave Persistente", + "Bronquiolitis", + "Bulimia", + "Cáncer", + "Enfermedades cardiorrespiratorias", + "Enfermedad celíaca", + "Parálisis cerebral", + "labio hendido o paladar hendido", + "Enfermedad de Crohn", + "Fibrosis quística", + "Depresión", + "Discapacidades del desarrollo sensorial o motora", + "Diabetes Mellitus", + "Síndrome de Down", + "Nivel elevado de plomo en sangre", + "Epilepsia", + "Síndrome de alcoholismo fetal", + "Enfermedad de la vesícula biliar", + "Enfermedad por reflujo gastroesofágico", + "Anormalidades gastrointestinales", + "Infección por VIH", + "Cardiopatía", + "Hepatitis", + "Hipertensión Crónica/Prehipertensión", + "Hipertensión inducida por el embarazo", + "Hipertiroidismo", + "hipoglucemia", + "Hipotiroidismo", + "Errores innatos del metabolismo", + "Enfermedad Inflamatoria Intestinal (EII)", + "Intolerancia a la lactosa", + "Grande para la edad gestacional", + "Enfermedad del hígado", + "Lupus eritematoso", + "Cirugía Mayor Quemaduras o Traumatismos", + "Síndromes de malabsorción", + "Meningitis", + "Esclerosis múltiple", + "Distrofia muscular", + "Síndrome de abstinencia neonatal", + "Defecto del tubo neural (espina bífida)", + "Enfermedades por deficiencia de nutrientes", + "Pancreatitis", + "Infección parasitaria", + "La enfermedad de Parkinson", + "Neumonía", + "prediabetes", + "Enfermedad renal", + "Anemia falciforme", + "Enterocolitis y síndrome del intestino delgado", + "Pequeño para la edad gestacional (PEG)", + "Talasemia mayor", + "Tuberculosis", + "Colitis ulcerosa", + "Úlceras Estómago o Intestinal", +}); + +const auto esARMedicalTests = std::to_array({ + "examen HEENT", + "palpación abdominal", + "amniocentesis", + "balistocardiograma", + "cápsula endoscópica", + "auscultación cardíaca", + "coloscopía", + "colposcopía", + "cateterismo coronario", + "cystoscopía", + "examinación rectal digital", + "ecocardiografía", + "electrocardiográma", + "electroencefalográma", + "electromiografía", + "colangiopancreatografía retrógrada endoscópica", + "estudio de motilidad esofágica", + "monitorización del pH esofágico", + "esofagogastroduodenoscopia", + "prueba de audición", + "histeroscopia", + "laparoscopia", + "laringoscopia", + "biopsia de hígado", + "mamografía", + "examen neurológico", + "pruebas neuropsicológicas", + "polisomnografía", + "evaluación psiquiátrica", + "auscultación pulmonar", + "pletismografía pulmonar", + "biopsia de piel", + "toracocentesis", + "pruebas urodinámicas", + "examen vaginal", +}); + +const auto esARSpecialties = std::to_array({ + "Medicina de emergencia y accidentes", + "Alergias", + "Estética", + "Cardiología", + "Psiquatría infantil", + "Biología Clínica", + "Química Clínica", + "Microbiología Clínica", + "Neurofisiología Clínica", + "Cirugía Creaneofacial", + "Dermatología", + "Endocrinología", + "Medicina General y Familiar", + "Cirugía Gastroenterológica", + "Gastroenterolgía", + "Práctica General", + "Cirugía General", + "Geriátrico", + "Hematología", + "Immunología", + "Enfermedades Infecciosas", + "Medicina Interna", + "Medicinal de Laboratorio", + "Nefrología", + "Neurología", + "Neurosiquiatría", + "Neurocirugía", + "Medicina Nuclear", + "Ginecología y Obstetricia", + "Medicina Ocupacional", + "Oncología", + "Oftalmología", + "Cirugía Oral y maxilofacial", + "Ortopedía", + "Otorrinolaringología", + "Cirugía Pediátrica", + "Pediatría", + "Patología", + "Farmacología", + "Medicina física y de rehabilitación", + "Cirugía Plástica", + "Cirugía podológica", + "Medicina Preventiva", + "Psiquiatráa", + "Salud Pública", + "Oncología Radioterápica", + "Radiología", + "Medicina Respiratoría", + "Reumatología", + "Estomatología", + "Cirugía Toráxica", + "Medicina Tropical", + "Urología", + "Cirugía Vascular", + "Venereología", +}); + +const MedicineDefinition esARMedicineDefinition = { + .medicalConditions = esARMedicalConditions, .medicalTests = esARMedicalTests, .specialties = esARSpecialties}; + +//"pt_BR" +const auto ptBRMedicalConditions = std::to_array( + {"AIDS", "Anorexia nervosa", "Artrite Reumatóide Juvenil", "Asma Persistente Moderada ou Grave", "bronquiolite", + "Bulimia", "Câncer", "Doenças cardiorrespiratórias", "Doença celíaca", "Paralisia cerebral", + "lábio leporino ou fenda palatina", "Doença de Crohn", "Fibrose cística", "Depressão", + "Deficiências de desenvolvimento sensorial ou motor", "Diabetes Mellitus", "Síndrome de Down", + "Úlceras estomacais ou intestinais"}); + +const auto ptBRMedicalTests = std::to_array({"exame HEENT", + "palpação abdominal", + "amniocentese", + "balistocardiograma", + "endoscopia por cápsula", + "ausculta cardíaca", + "coloscopia", + "colposcopia", + "cateterismo coronário", + "cistoscopia", + "exame retal digital", + "ecocardiografia", + "eletrocardiograma", + "eletroencefalograma", + "eletromiografia", + "colangiopancreatografia retrógrada endoscópica", + "estudo da motilidade esofágica", + "monitoramento do pH esofágico", + "esofagogastroduodenoscopia", + "teste de audição", + "histeroscopia", + "laparoscopia", + "laringoscopia", + "biópsia hepática", + "mamografia", + "exame neurológico", + "testes neuropsicológicos", + "polissonografia", + "avaliação psiquiátrica", + "ausculta pulmonar", + "pletismografia pulmonar", + "biópsia de pele", + "toracocentese", + "testes urodinâmicos", + "exame vaginal"}); + +const auto ptBRSpecialties = std::to_array({ + "Medicina de emergência e acidentes", + "Alergias", + "Estética", + "Cardiologia", + "Psiquiatria infantil", + "Biologia Clínica", + "Química Clínica", + "Microbiologia Clínica", + "Neurofisiologia Clínica", + "Cirurgia Creneofacial", + "Dermatologia", + "Endocrinologia", + "Medicina Geral e Familiar", + "Cirurgia Gastroenterológica", + "Gastroenterologia", + "Clínica Geral", + "Cirurgia Geral", + "Geriátrica", + "Hematologia", + "Imunologia", + "Doenças Infecciosas", + "Medicina interna", + "Medicina Laboratorial", + "Nefrologia", + "Neurologia", + "Neuropsiquiatria", + "Neurocirurgia", + "Medicina Nuclear", + "Ginecologia e Obstetrícia", + "Medicina Ocupacional", + "Oncologia", + "Oftalmologia", + "Cirurgia Oral e Maxilofacial", + "Ortopedia", + "Otorrinolaringologia", + "Cirurgia Pediátrica", + "Pediatria", + "Patologia", + "Farmacologia", + "Medicina Física e de Reabilitação", + "Cirurgia plástica", + "Cirurgia Podiátrica", + "Medicina preventiva", + "Psiquiatria", + "Saúde pública", + "Oncologia de Radiação", + "Radiologia", + "Medicina Respiratória", + "Reumatologia", + "Estomatologia", + "Cirurgia Toráxica", + "Medicina Tropical", + "Urologia", + "Cirurgia Vascular", + "Venereologia", +}); + +const MedicineDefinition ptBRMedicineDefinition = { + .medicalConditions = ptBRMedicalConditions, .medicalTests = ptBRMedicalTests, .specialties = ptBRSpecialties}; + +const std::map medicineMapSpan({ + {faker::Locale::en_US, {enUSMedicineDefinition}}, {faker::Locale::pt_BR, {ptBRMedicineDefinition}}, + {faker::Locale::es_AR, {esARMedicineDefinition}}, {faker::Locale::es_BO, {esARMedicineDefinition}}, + {faker::Locale::es_CL, {esARMedicineDefinition}}, {faker::Locale::es_CO, {esARMedicineDefinition}}, + {faker::Locale::es_CR, {esARMedicineDefinition}}, {faker::Locale::es_DO, {esARMedicineDefinition}}, + {faker::Locale::es_EC, {esARMedicineDefinition}}, {faker::Locale::es_ES, {esARMedicineDefinition}}, + {faker::Locale::es_GT, {esARMedicineDefinition}}, {faker::Locale::es_HN, {esARMedicineDefinition}}, + {faker::Locale::es_MX, {esARMedicineDefinition}}, {faker::Locale::es_NI, {esARMedicineDefinition}}, + {faker::Locale::es_PA, {esARMedicineDefinition}}, {faker::Locale::es_PE, {esARMedicineDefinition}}, + {faker::Locale::es_PR, {esARMedicineDefinition}}, {faker::Locale::es_PY, {esARMedicineDefinition}}, + {faker::Locale::es_US, {esARMedicineDefinition}}, {faker::Locale::es_SV, {esARMedicineDefinition}}, + {faker::Locale::es_UY, {esARMedicineDefinition}}, {faker::Locale::es_VE, {esARMedicineDefinition}}, +}); + } diff --git a/tests/modules/medicine_test.cpp b/tests/modules/medicine_test.cpp index df3e9026e..bd033d12d 100644 --- a/tests/modules/medicine_test.cpp +++ b/tests/modules/medicine_test.cpp @@ -6,36 +6,68 @@ #include "gtest/gtest.h" #include "medicine_data.h" +#include "locale.h" using namespace ::testing; +using namespace faker; using namespace faker::medicine; -class MedicineTest : public Test +namespace +{ + +const struct MedicineDefinition& getMedicineMapDefinition(Locale locale) +{ + if (medicineMapSpan.find(locale) == medicineMapSpan.end()) + { + return medicineMapSpan.at(Locale::en_US); + } + else + { + return medicineMapSpan.at(locale); + } +} +} +class MedicineTestLocale : public TestWithParam { public: }; -TEST_F(MedicineTest, shouldGenerateMedicalCondition) + +TEST_P(MedicineTestLocale, shouldGenerateMedicalConditionLocale) { - const auto generatedMedicalCondition = condition(); + auto extra = GetParam(); + auto generatedMedicalCondition = condition(extra); + auto medicalDefinition = getMedicineMapDefinition(extra); - ASSERT_TRUE(std::ranges::any_of(medicalConditions, - [generatedMedicalCondition](const std::string_view& medicalCondition) + ASSERT_TRUE(std::ranges::any_of(medicalDefinition.medicalConditions, [generatedMedicalCondition](const std::string_view& medicalCondition) { return medicalCondition == generatedMedicalCondition; })); } -TEST_F(MedicineTest, shouldGenerateMedicalTest) + +TEST_P(MedicineTestLocale, shouldGenerateMedicalTestLocale) { - const auto generatedMedicalTest = medicalTest(); + auto extra = GetParam(); + + const auto generatedMedicalTest = medicalTest(extra); + + auto medicalDefinition = getMedicineMapDefinition(extra); - ASSERT_TRUE(std::ranges::any_of(medicalTests, [generatedMedicalTest](const std::string_view& medicalTest) + ASSERT_TRUE(std::ranges::any_of(medicalDefinition.medicalTests, [generatedMedicalTest](const std::string_view& medicalTest) { return medicalTest == generatedMedicalTest; })); } -TEST_F(MedicineTest, shouldGenerateSpecialty) +TEST_P(MedicineTestLocale, shouldGenerateSpecialtyLocale) { - const auto generatedSpecialty = specialty(); + auto extra = GetParam(); - ASSERT_TRUE(std::ranges::any_of(specialties, [generatedSpecialty](const std::string_view& specialty) + const auto generatedSpecialty = specialty(extra); + + auto medicalDefinition = getMedicineMapDefinition(extra); + + ASSERT_TRUE(std::ranges::any_of(medicalDefinition.specialties, [generatedSpecialty](const std::string_view& specialty) { return specialty == generatedSpecialty; })); } + + +INSTANTIATE_TEST_SUITE_P(testMedicineByLocale, MedicineTestLocale, ValuesIn(locales), + [](const TestParamInfo& paramInfo) { return toString(paramInfo.param); }); \ No newline at end of file From dbc3898e05fc6954e1a27cb6e33b6b867f6c2f28 Mon Sep 17 00:00:00 2001 From: Cobollatin <40482066+Cobollatin@users.noreply.github.com> Date: Fri, 29 Nov 2024 20:37:04 +0100 Subject: [PATCH 2/7] feat: ULID (#1003) * first approach * doxygen * bazel * fixes * revert bazel * shared build * format * bash header --- LICENSES.md | 24 ++ include/faker-cxx/string.h | 20 ++ include/helpers/ulid/ulid.h | 15 ++ include/helpers/ulid/ulid_struct.h | 362 ++++++++++++++++++++++++++++ include/helpers/ulid/ulid_uint128.h | 181 ++++++++++++++ scripts/format_code.sh | 2 + src/modules/string.cpp | 8 + tests/modules/string_test.cpp | 12 + 8 files changed, 624 insertions(+) create mode 100644 include/helpers/ulid/ulid.h create mode 100644 include/helpers/ulid/ulid_struct.h create mode 100644 include/helpers/ulid/ulid_uint128.h diff --git a/LICENSES.md b/LICENSES.md index 692c63f18..af716c3ea 100644 --- a/LICENSES.md +++ b/LICENSES.md @@ -1,5 +1,29 @@ # Third Party Licenses +## suyash/ulid: + +MIT License + +Copyright (c) 2016 Suyash + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + ## flexxxxer/Md5Hash: MIT License diff --git a/include/faker-cxx/string.h b/include/faker-cxx/string.h index df0d8c018..827155488 100644 --- a/include/faker-cxx/string.h +++ b/include/faker-cxx/string.h @@ -6,8 +6,10 @@ #include #include #include +#include #include "faker-cxx/export.h" +#include "helpers/ulid/ulid.h" #include "random_generator.h" namespace faker::string @@ -118,6 +120,24 @@ std::string uuid(RandomGenerator gen = RandomGenerator{}) return result; } +#ifdef __SIZEOF_INT128__ +#define ULIDUINT128 +#endif + +/** + * @brief Generates an Universally Unique Lexicographically Sortable Identifier. + * + * @param refDate A reference date (type time_t) + * + * @returns ULID UINT128. + * + * @code + * faker::string::ulid() // "0001C7STHC0G2081040G208104" + * faker::string::ulid(1484581420) // "0001C7STHC0G2081040G208104" + * @endcode + */ +FAKER_CXX_EXPORT std::string ulid(time_t refDate = std::time(nullptr)); // Based on https://github.com/suyash/ulid + /** * @brief Returns a string containing UTF-16 chars between 33 and 125 (`!` to `}`). * diff --git a/include/helpers/ulid/ulid.h b/include/helpers/ulid/ulid.h new file mode 100644 index 000000000..30276a98d --- /dev/null +++ b/include/helpers/ulid/ulid.h @@ -0,0 +1,15 @@ +#ifndef ULID_HH +#define ULID_HH + +// http://stackoverflow.com/a/23981011 +#ifdef __SIZEOF_INT128__ +#define ULIDUINT128 +#endif + +#ifdef ULIDUINT128 +#include "ulid_uint128.h" +#else +#include "ulid_struct.h" +#endif // ULIDUINT128 + +#endif // ULID_HH \ No newline at end of file diff --git a/include/helpers/ulid/ulid_struct.h b/include/helpers/ulid/ulid_struct.h new file mode 100644 index 000000000..659e5442f --- /dev/null +++ b/include/helpers/ulid/ulid_struct.h @@ -0,0 +1,362 @@ +#ifndef ULID_STRUCT_HH +#define ULID_STRUCT_HH + +#include +#include +#include +#include +#include +#include + +#if _MSC_VER > 0 +typedef uint32_t rand_t; +# else +typedef uint8_t rand_t; +#endif + +namespace faker::helpers::ulid { + +/** + * ULID is a 16 byte Universally Unique Lexicographically Sortable Identifier + * */ +struct ULID { + uint8_t data[16]; + + ULID() { + // unrolled loop + data[0] = 0; + data[1] = 0; + data[2] = 0; + data[3] = 0; + data[4] = 0; + data[5] = 0; + data[6] = 0; + data[7] = 0; + data[8] = 0; + data[9] = 0; + data[10] = 0; + data[11] = 0; + data[12] = 0; + data[13] = 0; + data[14] = 0; + data[15] = 0; + } + + ULID(uint64_t val) { + // unrolled loop + data[15] = static_cast(val); + + val >>= 8; + data[14] = static_cast(val); + + val >>= 8; + data[13] = static_cast(val); + + val >>= 8; + data[12] = static_cast(val); + + val >>= 8; + data[11] = static_cast(val); + + val >>= 8; + data[10] = static_cast(val); + + val >>= 8; + data[9] = static_cast(val); + + val >>= 8; + data[8] = static_cast(val); + + data[7] = 0; + data[6] = 0; + data[5] = 0; + data[4] = 0; + data[3] = 0; + data[2] = 0; + data[1] = 0; + data[0] = 0; + } + + ULID(const ULID& other) { + // for (int i = 0 ; i < 16 ; i++) { + // data[i] = other.data[i]; + // } + + // unrolled loop + data[0] = other.data[0]; + data[1] = other.data[1]; + data[2] = other.data[2]; + data[3] = other.data[3]; + data[4] = other.data[4]; + data[5] = other.data[5]; + data[6] = other.data[6]; + data[7] = other.data[7]; + data[8] = other.data[8]; + data[9] = other.data[9]; + data[10] = other.data[10]; + data[11] = other.data[11]; + data[12] = other.data[12]; + data[13] = other.data[13]; + data[14] = other.data[14]; + data[15] = other.data[15]; + } + + ULID& operator=(const ULID& other) { + // for (int i = 0 ; i < 16 ; i++) { + // data[i] = other.data[i]; + // } + + // unrolled loop + data[0] = other.data[0]; + data[1] = other.data[1]; + data[2] = other.data[2]; + data[3] = other.data[3]; + data[4] = other.data[4]; + data[5] = other.data[5]; + data[6] = other.data[6]; + data[7] = other.data[7]; + data[8] = other.data[8]; + data[9] = other.data[9]; + data[10] = other.data[10]; + data[11] = other.data[11]; + data[12] = other.data[12]; + data[13] = other.data[13]; + data[14] = other.data[14]; + data[15] = other.data[15]; + + return *this; + } + + ULID(ULID&& other) { + // for (int i = 0 ; i < 16 ; i++) { + // data[i] = other.data[i]; + // other.data[i] = 0; + // } + + // unrolled loop + data[0] = other.data[0]; + other.data[0] = 0; + + data[1] = other.data[1]; + other.data[1] = 0; + + data[2] = other.data[2]; + other.data[2] = 0; + + data[3] = other.data[3]; + other.data[3] = 0; + + data[4] = other.data[4]; + other.data[4] = 0; + + data[5] = other.data[5]; + other.data[5] = 0; + + data[6] = other.data[6]; + other.data[6] = 0; + + data[7] = other.data[7]; + other.data[7] = 0; + + data[8] = other.data[8]; + other.data[8] = 0; + + data[9] = other.data[9]; + other.data[9] = 0; + + data[10] = other.data[10]; + other.data[10] = 0; + + data[11] = other.data[11]; + other.data[11] = 0; + + data[12] = other.data[12]; + other.data[12] = 0; + + data[13] = other.data[13]; + other.data[13] = 0; + + data[14] = other.data[14]; + other.data[14] = 0; + + data[15] = other.data[15]; + other.data[15] = 0; + } + + ULID& operator=(ULID&& other) { + // for (int i = 0 ; i < 16 ; i++) { + // data[i] = other.data[i]; + // other.data[i] = 0; + // } + + // unrolled loop + data[0] = other.data[0]; + other.data[0] = 0; + + data[1] = other.data[1]; + other.data[1] = 0; + + data[2] = other.data[2]; + other.data[2] = 0; + + data[3] = other.data[3]; + other.data[3] = 0; + + data[4] = other.data[4]; + other.data[4] = 0; + + data[5] = other.data[5]; + other.data[5] = 0; + + data[6] = other.data[6]; + other.data[6] = 0; + + data[7] = other.data[7]; + other.data[7] = 0; + + data[8] = other.data[8]; + other.data[8] = 0; + + data[9] = other.data[9]; + other.data[9] = 0; + + data[10] = other.data[10]; + other.data[10] = 0; + + data[11] = other.data[11]; + other.data[11] = 0; + + data[12] = other.data[12]; + other.data[12] = 0; + + data[13] = other.data[13]; + other.data[13] = 0; + + data[14] = other.data[14]; + other.data[14] = 0; + + data[15] = other.data[15]; + other.data[15] = 0; + + return *this; + } +}; + +/** + * EncodeTime will encode the first 6 bytes of a uint8_t array to the passed + * timestamp + * */ +inline void EncodeTime(time_t timestamp, ULID& ulid) { + ulid.data[0] = static_cast(timestamp >> 40); + ulid.data[1] = static_cast(timestamp >> 32); + ulid.data[2] = static_cast(timestamp >> 24); + ulid.data[3] = static_cast(timestamp >> 16); + ulid.data[4] = static_cast(timestamp >> 8); + ulid.data[5] = static_cast(timestamp); +} + +/** + * EncodeEntropy will encode the last 10 bytes of the passed uint8_t array with + * the values generated using the passed random number generator. + * */ +inline void EncodeEntropy(const std::function& rng, ULID& ulid) { + ulid.data[6] = rng(); + ulid.data[7] = rng(); + ulid.data[8] = rng(); + ulid.data[9] = rng(); + ulid.data[10] = rng(); + ulid.data[11] = rng(); + ulid.data[12] = rng(); + ulid.data[13] = rng(); + ulid.data[14] = rng(); + ulid.data[15] = rng(); +} + +/** + * Encode will create an encoded ULID with a timestamp and a generator. + * */ +inline void Encode(time_t timestamp, const std::function& rng, ULID& ulid) { + EncodeTime(timestamp, ulid); + EncodeEntropy(rng, ulid); +} + +/** + * Create will create a ULID with a timestamp and a generator. + * */ +inline ULID Create(time_t timestamp, const std::function& rng) { + ULID ulid; + Encode(timestamp, rng, ulid); + return ulid; +} + +/** + * Crockford's Base32 + * */ +static const char Encoding[33] = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"; + +/** + * MarshalTo will marshal a ULID to the passed character array. + * + * Implementation taken directly from oklog/ulid + * (https://sourcegraph.com/github.com/oklog/ulid@0774f81f6e44af5ce5e91c8d7d76cf710e889ebb/-/blob/ulid.go#L162-190) + * + * timestamp:
+ * dst[0]: first 3 bits of data[0]
+ * dst[1]: last 5 bits of data[0]
+ * dst[2]: first 5 bits of data[1]
+ * dst[3]: last 3 bits of data[1] + first 2 bits of data[2]
+ * dst[4]: bits 3-7 of data[2]
+ * dst[5]: last bit of data[2] + first 4 bits of data[3]
+ * dst[6]: last 4 bits of data[3] + first bit of data[4]
+ * dst[7]: bits 2-6 of data[4]
+ * dst[8]: last 2 bits of data[4] + first 3 bits of data[5]
+ * dst[9]: last 5 bits of data[5]
+ * + * entropy: + * follows similarly, except now all components are set to 5 bits. + * */ +inline void MarshalTo(const ULID& ulid, char dst[26]) { + // 10 byte timestamp + dst[0] = Encoding[(ulid.data[0] & 224) >> 5]; + dst[1] = Encoding[ulid.data[0] & 31]; + dst[2] = Encoding[(ulid.data[1] & 248) >> 3]; + dst[3] = Encoding[((ulid.data[1] & 7) << 2) | ((ulid.data[2] & 192) >> 6)]; + dst[4] = Encoding[(ulid.data[2] & 62) >> 1]; + dst[5] = Encoding[((ulid.data[2] & 1) << 4) | ((ulid.data[3] & 240) >> 4)]; + dst[6] = Encoding[((ulid.data[3] & 15) << 1) | ((ulid.data[4] & 128) >> 7)]; + dst[7] = Encoding[(ulid.data[4] & 124) >> 2]; + dst[8] = Encoding[((ulid.data[4] & 3) << 3) | ((ulid.data[5] & 224) >> 5)]; + dst[9] = Encoding[ulid.data[5] & 31]; + + // 16 bytes of entropy + dst[10] = Encoding[(ulid.data[6] & 248) >> 3]; + dst[11] = Encoding[((ulid.data[6] & 7) << 2) | ((ulid.data[7] & 192) >> 6)]; + dst[12] = Encoding[(ulid.data[7] & 62) >> 1]; + dst[13] = Encoding[((ulid.data[7] & 1) << 4) | ((ulid.data[8] & 240) >> 4)]; + dst[14] = Encoding[((ulid.data[8] & 15) << 1) | ((ulid.data[9] & 128) >> 7)]; + dst[15] = Encoding[(ulid.data[9] & 124) >> 2]; + dst[16] = Encoding[((ulid.data[9] & 3) << 3) | ((ulid.data[10] & 224) >> 5)]; + dst[17] = Encoding[ulid.data[10] & 31]; + dst[18] = Encoding[(ulid.data[11] & 248) >> 3]; + dst[19] = Encoding[((ulid.data[11] & 7) << 2) | ((ulid.data[12] & 192) >> 6)]; + dst[20] = Encoding[(ulid.data[12] & 62) >> 1]; + dst[21] = Encoding[((ulid.data[12] & 1) << 4) | ((ulid.data[13] & 240) >> 4)]; + dst[22] = Encoding[((ulid.data[13] & 15) << 1) | ((ulid.data[14] & 128) >> 7)]; + dst[23] = Encoding[(ulid.data[14] & 124) >> 2]; + dst[24] = Encoding[((ulid.data[14] & 3) << 3) | ((ulid.data[15] & 224) >> 5)]; + dst[25] = Encoding[ulid.data[15] & 31]; +} + +/** + * Marshal will marshal a ULID to a std::string. + * */ +inline std::string Marshal(const ULID& ulid) { + char data[27]; + data[26] = '\0'; + MarshalTo(ulid, data); + return std::string(data); +} + +}; // namespace ulid + +#endif // ULID_STRUCT_HH \ No newline at end of file diff --git a/include/helpers/ulid/ulid_uint128.h b/include/helpers/ulid/ulid_uint128.h new file mode 100644 index 000000000..51fb14ee9 --- /dev/null +++ b/include/helpers/ulid/ulid_uint128.h @@ -0,0 +1,181 @@ +#ifndef ULID_UINT128_HH +#define ULID_UINT128_HH + +#include +#include +#include +#include +#include +#include + +#if _MSC_VER > 0 +typedef uint32_t rand_t; +# else +typedef uint8_t rand_t; +#endif + +namespace faker::helpers::ulid { + +/** + * ULID is a 16 byte Universally Unique Lexicographically Sortable Identifier + * */ +typedef __uint128_t ULID; + +/** + * EncodeTime will encode the first 6 bytes of a uint8_t array to the passed + * timestamp + * */ +inline void EncodeTime(time_t timestamp, ULID& ulid) { + ULID t = static_cast(timestamp >> 40); + + t <<= 8; + t |= static_cast(timestamp >> 32); + + t <<= 8; + t |= static_cast(timestamp >> 24); + + t <<= 8; + t |= static_cast(timestamp >> 16); + + t <<= 8; + t |= static_cast(timestamp >> 8); + + t <<= 8; + t |= static_cast(timestamp); + + t <<= 80; + + ULID mask = 1; + mask <<= 80; + mask--; + + ulid = t | (ulid & mask); +} + +/** + * EncodeEntropy will encode the last 10 bytes of the passed uint8_t array with + * the values generated using the passed random number generator. + * */ +inline void EncodeEntropy(const std::function& rng, ULID& ulid) { + ulid = (ulid >> 80) << 80; + + ULID e = rng(); + + e <<= 8; + e |= rng(); + + e <<= 8; + e |= rng(); + + e <<= 8; + e |= rng(); + + e <<= 8; + e |= rng(); + + e <<= 8; + e |= rng(); + + e <<= 8; + e |= rng(); + + e <<= 8; + e |= rng(); + + e <<= 8; + e |= rng(); + + e <<= 8; + e |= rng(); + + ulid |= e; +} + +/** + * Encode will create an encoded ULID with a timestamp and a generator. + * */ +inline void Encode(time_t timestamp, const std::function& rng, ULID& ulid) { + EncodeTime(timestamp, ulid); + EncodeEntropy(rng, ulid); +} + +/** + * Create will create a ULID with a timestamp and a generator. + * */ +inline ULID Create(time_t timestamp, const std::function& rng) { + ULID ulid = 0; + Encode(timestamp, rng, ulid); + return ulid; +} + +/** + * Crockford's Base32 + * */ +static const char Encoding[33] = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"; + +/** + * MarshalTo will marshal a ULID to the passed character array. + * + * Implementation taken directly from oklog/ulid + * (https://sourcegraph.com/github.com/oklog/ulid@0774f81f6e44af5ce5e91c8d7d76cf710e889ebb/-/blob/ulid.go#L162-190) + * + * timestamp: + * dst[0]: first 3 bits of data[0] + * dst[1]: last 5 bits of data[0] + * dst[2]: first 5 bits of data[1] + * dst[3]: last 3 bits of data[1] + first 2 bits of data[2] + * dst[4]: bits 3-7 of data[2] + * dst[5]: last bit of data[2] + first 4 bits of data[3] + * dst[6]: last 4 bits of data[3] + first bit of data[4] + * dst[7]: bits 2-6 of data[4] + * dst[8]: last 2 bits of data[4] + first 3 bits of data[5] + * dst[9]: last 5 bits of data[5] + * + * entropy: + * follows similarly, except now all components are set to 5 bits. + * */ +inline void MarshalTo(const ULID& ulid, char dst[26]) { + // 10 byte timestamp + dst[0] = Encoding[(static_cast(ulid >> 120) & 224) >> 5]; + dst[1] = Encoding[static_cast(ulid >> 120) & 31]; + dst[2] = Encoding[(static_cast(ulid >> 112) & 248) >> 3]; + dst[3] = Encoding[((static_cast(ulid >> 112) & 7) << 2) | ((static_cast(ulid >> 104) & 192) >> 6)]; + dst[4] = Encoding[(static_cast(ulid >> 104) & 62) >> 1]; + dst[5] = Encoding[((static_cast(ulid >> 104) & 1) << 4) | ((static_cast(ulid >> 96) & 240) >> 4)]; + dst[6] = Encoding[((static_cast(ulid >> 96) & 15) << 1) | ((static_cast(ulid >> 88) & 128) >> 7)]; + dst[7] = Encoding[(static_cast(ulid >> 88) & 124) >> 2]; + dst[8] = Encoding[((static_cast(ulid >> 88) & 3) << 3) | ((static_cast(ulid >> 80) & 224) >> 5)]; + dst[9] = Encoding[static_cast(ulid >> 80) & 31]; + + // 16 bytes of entropy + dst[10] = Encoding[(static_cast(ulid >> 72) & 248) >> 3]; + dst[11] = Encoding[((static_cast(ulid >> 72) & 7) << 2) | ((static_cast(ulid >> 64) & 192) >> 6)]; + dst[12] = Encoding[(static_cast(ulid >> 64) & 62) >> 1]; + dst[13] = Encoding[((static_cast(ulid >> 64) & 1) << 4) | ((static_cast(ulid >> 56) & 240) >> 4)]; + dst[14] = Encoding[((static_cast(ulid >> 56) & 15) << 1) | ((static_cast(ulid >> 48) & 128) >> 7)]; + dst[15] = Encoding[(static_cast(ulid >> 48) & 124) >> 2]; + dst[16] = Encoding[((static_cast(ulid >> 48) & 3) << 3) | ((static_cast(ulid >> 40) & 224) >> 5)]; + dst[17] = Encoding[static_cast(ulid >> 40) & 31]; + dst[18] = Encoding[(static_cast(ulid >> 32) & 248) >> 3]; + dst[19] = Encoding[((static_cast(ulid >> 32) & 7) << 2) | ((static_cast(ulid >> 24) & 192) >> 6)]; + dst[20] = Encoding[(static_cast(ulid >> 24) & 62) >> 1]; + dst[21] = Encoding[((static_cast(ulid >> 24) & 1) << 4) | ((static_cast(ulid >> 16) & 240) >> 4)]; + dst[22] = Encoding[((static_cast(ulid >> 16) & 15) << 1) | ((static_cast(ulid >> 8) & 128) >> 7)]; + dst[23] = Encoding[(static_cast(ulid >> 8) & 124) >> 2]; + dst[24] = Encoding[((static_cast(ulid >> 8) & 3) << 3) | (((static_cast(ulid)) & 224) >> 5)]; + dst[25] = Encoding[(static_cast(ulid)) & 31]; +} + +/** + * Marshal will marshal a ULID to a std::string. + * */ +inline std::string Marshal(const ULID& ulid) { + char data[27]; + data[26] = '\0'; + MarshalTo(ulid, data); + return std::string(data); +} + +}; // namespace ulid + +#endif // ULID_UINT128_HH \ No newline at end of file diff --git a/scripts/format_code.sh b/scripts/format_code.sh index 836c7910e..df393dafa 100755 --- a/scripts/format_code.sh +++ b/scripts/format_code.sh @@ -1 +1,3 @@ +#!/usr/bin/bash + clang-format src/**/*.cpp src/**/*.h include/**/*.h -i -style=file diff --git a/src/modules/string.cpp b/src/modules/string.cpp index 62739a54f..fa7160775 100644 --- a/src/modules/string.cpp +++ b/src/modules/string.cpp @@ -10,6 +10,7 @@ #include "common/algo_helper.h" #include "faker-cxx/helper.h" #include "faker-cxx/number.h" +#include "helpers/ulid/ulid.h" #include "string_data.h" namespace faker::string @@ -358,4 +359,11 @@ std::string nanoId(int minLength, int maxLength) return id; } +std::string ulid(time_t refDate) +{ + const auto uild = faker::helpers::ulid::Create(refDate, []() { return 4; }); + std::string data = faker::helpers::ulid::Marshal(uild); + return std::string(data); +} + } diff --git a/tests/modules/string_test.cpp b/tests/modules/string_test.cpp index 69b712f04..b7f6e9932 100644 --- a/tests/modules/string_test.cpp +++ b/tests/modules/string_test.cpp @@ -124,6 +124,18 @@ TEST_F(StringTest, shouldGenerateUuid4) ASSERT_EQ(generatedUuid[23], '-'); } +TEST_F(StringTest, shouldGenerateUlidNoArguments) +{ + const auto generatedUlidNoArg = ulid(); + ASSERT_EQ(generatedUlidNoArg.length(), 26); +} + +TEST_F(StringTest, shouldGenerateUlid) +{ + const auto generatedUlid = ulid(1484581420); + ASSERT_EQ(generatedUlid, "0001C7STHC0G2081040G208104"); +} + TEST_F(StringTest, ShouldGenerateSymbolStringDefault) { const auto generatedSymbol = symbol(); From 63b3e3ac5d6026c0f2a8fb7342f2eef2d6ff07c2 Mon Sep 17 00:00:00 2001 From: Cobollatin <40482066+Cobollatin@users.noreply.github.com> Date: Sun, 1 Dec 2024 08:56:10 +0100 Subject: [PATCH 3/7] feat: UUID V1 and V3 (#1004) * first approach * doxygen * bazel * fixes * revert bazel * shared build * format * bash header * uuid-v1 * format * uuid-v1 * uuid-v3 * fixes --- include/faker-cxx/string.h | 82 ++++++++------------ src/modules/string.cpp | 141 ++++++++++++++++++++++++++++++++++ tests/modules/string_test.cpp | 129 +++++++++---------------------- 3 files changed, 210 insertions(+), 142 deletions(-) diff --git a/include/faker-cxx/string.h b/include/faker-cxx/string.h index 827155488..d208ba334 100644 --- a/include/faker-cxx/string.h +++ b/include/faker-cxx/string.h @@ -1,11 +1,19 @@ #pragma once +#include +#include +#include +#include +#include +#include #include #include #include #include #include +#include #include +#include #include #include "faker-cxx/export.h" @@ -14,6 +22,18 @@ namespace faker::string { + +enum class Uuid +{ + V1, // Version 1: UUIDs using a timestamp and monotonic counter. + V3, // Version 3: UUIDs based on the MD5 hash of some data. + V4, // Version 4: UUIDs with random data. + V5, // Version 5: UUIDs based on the SHA1 hash of some data. + V6, // Version 6: UUIDs using a timestamp and monotonic counter (sortable). + V7, // Version 7: UUIDs using a Unix timestamp (sortable). + V8 // Version 8: UUIDs using user-defined data. +}; + enum class StringCasing { Mixed, @@ -65,64 +85,24 @@ FAKER_CXX_EXPORT bool isValidGuarantee(GuaranteeMap& guarantee, std::set& FAKER_CXX_EXPORT std::string generateAtLeastString(const GuaranteeMap& guarantee); /** - * @brief Generates an Universally Unique Identifier with version 4. + * @brief Generates an Universally Unique Identifier, defaults to V4. * * @param gen A random number generator (type RandomGenerator) * - * @returns UUID v4. + * @returns UUID. * * @code - * faker::string::uuid() // "27666229-cedb-4a45-8018-98b1e1d921e2" + * faker::string::uuid() // "27666229-cedb-4a45-8018-98b1e1d921e2" // V4 + * faker::string::uuid(Uuid::V1) // "04f916a0-af32-11ef-9cd2-0242ac120002" + * faker::string::uuid(Uuid::V3) // "a3bb189e-8bf9-3888-9912-ace4e6543002" + * faker::string::uuid(Uuid::V4) // "27666229-cedb-4a45-8018-98b1e1d921e2" + * faker::string::uuid(Uuid::V5) // "27666229-cedb-4a45-8018-98b1e1d921e2" + * faker::string::uuid(Uuid::V6) // "27666229-cedb-4a45-8018-98b1e1d921e2" + * faker::string::uuid(Uuid::V7) // "27666229-cedb-4a45-8018-98b1e1d921e2" + * faker::string::uuid(Uuid::V8) // "27666229-cedb-4a45-8018-98b1e1d921e2" * @endcode */ -template -std::string uuid(RandomGenerator gen = RandomGenerator{}) -{ - static std::uniform_int_distribution<> dist(0, 15); - static std::uniform_int_distribution<> dist2(8, 11); - static std::string_view hexCharacters{"0123456789abcdef"}; - - std::string result; - result.reserve(36); - - for (int i = 0; i < 8; i++) - { - result.append(1, hexCharacters[static_cast(gen(dist))]); - } - result.append(1, '-'); - - for (int i = 0; i < 4; i++) - { - result.append(1, hexCharacters[static_cast(gen(dist))]); - } - result.append(1, '-'); - - result.append(1, '4'); - for (int i = 0; i < 3; i++) - { - result.append(1, hexCharacters[static_cast(gen(dist))]); - } - result.append(1, '-'); - - result.append(1, hexCharacters[static_cast(gen(dist2))]); - - for (int i = 0; i < 3; i++) - { - result.append(1, hexCharacters[static_cast(gen(dist))]); - } - result.append(1, '-'); - - for (int i = 0; i < 12; i++) - { - result.append(1, hexCharacters[static_cast(gen(dist))]); - } - - return result; -} - -#ifdef __SIZEOF_INT128__ -#define ULIDUINT128 -#endif +FAKER_CXX_EXPORT std::string uuid(Uuid uuid = Uuid::V4); /** * @brief Generates an Universally Unique Lexicographically Sortable Identifier. diff --git a/src/modules/string.cpp b/src/modules/string.cpp index fa7160775..025a32cb2 100644 --- a/src/modules/string.cpp +++ b/src/modules/string.cpp @@ -366,4 +366,145 @@ std::string ulid(time_t refDate) return std::string(data); } +#pragma region UUID_IMPLEMENTATIONS + +std::string uuidV1() +{ + RandomGenerator gen = RandomGenerator{}; + // Get current timestamp in 100-nanosecond intervals since UUID epoch (15 Oct 1582) + const uint64_t UUID_EPOCH_OFFSET = + 0x01B21DD213814000ULL; // Number of 100-ns intervals between UUID epoch and Unix epoch + auto now = std::chrono::system_clock::now(); + auto since_epoch = now.time_since_epoch(); + + uint64_t timestamp = + UUID_EPOCH_OFFSET + + static_cast(std::chrono::duration_cast(since_epoch).count() * 10); + + // Generate clock sequence (14 bits) + std::uniform_int_distribution clock_seq_dist(0, 0x3FFF); + uint16_t clock_seq = gen(clock_seq_dist); + + // Generate node identifier (48 bits) + std::uniform_int_distribution node_dist(0, 0xFFFFFFFFFFFFULL); + uint64_t node = gen(node_dist) & 0xFFFFFFFFFFFFULL; + + uint32_t time_low = static_cast(timestamp & 0xFFFFFFFFULL); + uint16_t time_mid = static_cast((timestamp >> 32) & 0xFFFFULL); + uint16_t time_hi_and_version = static_cast((timestamp >> 48) & 0x0FFFULL); + time_hi_and_version |= (1 << 12); // Set the version number to 1 + + uint8_t clock_seq_low = clock_seq & 0xFF; + uint8_t clock_seq_hi_and_reserved = ((clock_seq >> 8) & 0x3F) | 0x80; // Set the variant to '10' + + std::ostringstream ss; + ss << std::hex << std::setfill('0'); + ss << std::setw(8) << time_low << '-'; + ss << std::setw(4) << time_mid << '-'; + ss << std::setw(4) << time_hi_and_version << '-'; + ss << std::setw(2) << static_cast(clock_seq_hi_and_reserved); + ss << std::setw(2) << static_cast(clock_seq_low) << '-'; + ss << std::setw(12) << std::setw(12) << node; + + return ss.str(); +} + +std::string uuidV3() +{ + RandomGenerator gen = RandomGenerator{}; + // FAking MD5 hash with random data from the generator is enough for this purpose + std::array hash; + std::uniform_int_distribution dist(0, 255); + + for (auto& byte : hash) + { + byte = gen(dist); + } + + hash[6] = (hash[6] & 0x0F) | 0x30; // Set the version to 3 + hash[8] = (hash[8] & 0x3F) | 0x80; // Set the variant to '10' + + std::ostringstream ss; + ss << std::hex << std::setfill('0'); + for (size_t i = 0; i < hash.size(); ++i) + { + ss << std::setw(2) << static_cast(hash[i]); + // Add hyphens at the appropriate positions + if (i == 3 || i == 5 || i == 7 || i == 9) + ss << '-'; + } + + return ss.str(); +} + +std::string uuidV4() +{ + RandomGenerator gen = RandomGenerator{}; + static std::uniform_int_distribution<> dist(0, 15); + static std::uniform_int_distribution<> dist2(8, 11); + static std::string_view hexCharacters{"0123456789abcdef"}; + + std::string result; + result.reserve(36); + + for (int i = 0; i < 8; i++) + { + result.append(1, hexCharacters[static_cast(gen(dist))]); + } + result.append(1, '-'); + + for (int i = 0; i < 4; i++) + { + result.append(1, hexCharacters[static_cast(gen(dist))]); + } + result.append(1, '-'); + + result.append(1, '4'); + for (int i = 0; i < 3; i++) + { + result.append(1, hexCharacters[static_cast(gen(dist))]); + } + result.append(1, '-'); + + result.append(1, hexCharacters[static_cast(gen(dist2))]); + + for (int i = 0; i < 3; i++) + { + result.append(1, hexCharacters[static_cast(gen(dist))]); + } + result.append(1, '-'); + + for (int i = 0; i < 12; i++) + { + result.append(1, hexCharacters[static_cast(gen(dist))]); + } + + return result; +} + +std::string uuid(Uuid uuid) +{ + switch (uuid) + { + case Uuid::V1: + return uuidV1(); + case Uuid::V3: + return uuidV3(); + case Uuid::V4: + return uuidV4(); + case Uuid::V5: + return uuidV4(); + case Uuid::V6: + return uuidV4(); + case Uuid::V7: + return uuidV4(); + case Uuid::V8: + return uuidV4(); + default: + return uuidV4(); + } +} + +#pragma endregion + } diff --git a/tests/modules/string_test.cpp b/tests/modules/string_test.cpp index b7f6e9932..27e9aee10 100644 --- a/tests/modules/string_test.cpp +++ b/tests/modules/string_test.cpp @@ -20,100 +20,47 @@ class StringTest : public Test const int runCount{100}; }; -TEST_F(StringTest, shouldUseCustomRandomGeneratorForUuid4) -{ - RandomGenerator gen1{}; - const auto generatedUuid1 = uuid(gen1); - - ASSERT_EQ(generatedUuid1[8], '-'); - ASSERT_EQ(generatedUuid1[13], '-'); - ASSERT_EQ(generatedUuid1[14], '4'); - ASSERT_EQ(generatedUuid1[18], '-'); - ASSERT_EQ(generatedUuid1[23], '-'); - - RandomGenerator gen2{}; - const auto generatedUuid2 = uuid(gen2); - - ASSERT_EQ(generatedUuid2[8], '-'); - ASSERT_EQ(generatedUuid2[13], '-'); - ASSERT_EQ(generatedUuid2[14], '4'); - ASSERT_EQ(generatedUuid2[18], '-'); - ASSERT_EQ(generatedUuid2[23], '-'); - - RandomGenerator gen3{}; - const auto generatedUuid3 = uuid(gen3); - - ASSERT_EQ(generatedUuid3[8], '-'); - ASSERT_EQ(generatedUuid3[13], '-'); - ASSERT_EQ(generatedUuid3[14], '4'); - ASSERT_EQ(generatedUuid3[18], '-'); - ASSERT_EQ(generatedUuid3[23], '-'); - - RandomGenerator gen4{}; - const auto generatedUuid4 = uuid(gen4); - - ASSERT_EQ(generatedUuid4[8], '-'); - ASSERT_EQ(generatedUuid4[13], '-'); - ASSERT_EQ(generatedUuid4[14], '4'); - ASSERT_EQ(generatedUuid4[18], '-'); - ASSERT_EQ(generatedUuid4[23], '-'); - - RandomGenerator gen5{}; - const auto generatedUuid5 = uuid(gen5); - - ASSERT_EQ(generatedUuid5[8], '-'); - ASSERT_EQ(generatedUuid5[13], '-'); - ASSERT_EQ(generatedUuid5[14], '4'); - ASSERT_EQ(generatedUuid5[18], '-'); - ASSERT_EQ(generatedUuid5[23], '-'); - - RandomGenerator gen6{}; - const auto generatedUuid6 = uuid(gen6); - - ASSERT_EQ(generatedUuid6[8], '-'); - ASSERT_EQ(generatedUuid6[13], '-'); - ASSERT_EQ(generatedUuid6[14], '4'); - ASSERT_EQ(generatedUuid6[18], '-'); - ASSERT_EQ(generatedUuid6[23], '-'); - - RandomGenerator gen7{}; - const auto generatedUuid7 = uuid(gen7); - - ASSERT_EQ(generatedUuid7[8], '-'); - ASSERT_EQ(generatedUuid7[13], '-'); - ASSERT_EQ(generatedUuid7[14], '4'); - ASSERT_EQ(generatedUuid7[18], '-'); - ASSERT_EQ(generatedUuid7[23], '-'); - - RandomGenerator gen8{}; - const auto generatedUuid8 = uuid(gen8); - - ASSERT_EQ(generatedUuid8[8], '-'); - ASSERT_EQ(generatedUuid8[13], '-'); - ASSERT_EQ(generatedUuid8[14], '4'); - ASSERT_EQ(generatedUuid8[18], '-'); - ASSERT_EQ(generatedUuid8[23], '-'); - - RandomGenerator gen9{}; - const auto generatedUuid9 = uuid(gen9); - - ASSERT_EQ(generatedUuid9[8], '-'); - ASSERT_EQ(generatedUuid9[13], '-'); - ASSERT_EQ(generatedUuid9[14], '4'); - ASSERT_EQ(generatedUuid9[18], '-'); - ASSERT_EQ(generatedUuid9[23], '-'); - - RandomGenerator gen10{}; - const auto generatedUuid10 = uuid(gen10); - - ASSERT_EQ(generatedUuid10[8], '-'); - ASSERT_EQ(generatedUuid10[13], '-'); - ASSERT_EQ(generatedUuid10[14], '4'); - ASSERT_EQ(generatedUuid10[18], '-'); - ASSERT_EQ(generatedUuid10[23], '-'); +TEST_F(StringTest, shouldGenerateUuid1) +{ + const auto generatedUuid = uuid(Uuid::V1); + + // Ensure the UUID has the correct format + ASSERT_EQ(generatedUuid.size(), 36); + ASSERT_EQ(generatedUuid[8], '-'); + ASSERT_EQ(generatedUuid[13], '-'); + ASSERT_EQ(generatedUuid[18], '-'); + ASSERT_EQ(generatedUuid[23], '-'); + + ASSERT_EQ(generatedUuid[14], '1'); +} + +TEST_F(StringTest, shouldGenerateUuid3) +{ + const auto generatedUuid = uuid(Uuid::V3); + + ASSERT_EQ(generatedUuid.size(), 36); + ASSERT_EQ(generatedUuid[8], '-'); + ASSERT_EQ(generatedUuid[13], '-'); + ASSERT_EQ(generatedUuid[18], '-'); + ASSERT_EQ(generatedUuid[23], '-'); + + // Check the version number + ASSERT_EQ(generatedUuid[14], '3'); } TEST_F(StringTest, shouldGenerateUuid4) +{ + const auto generatedUuid = uuid(Uuid::V4); + + ASSERT_EQ(generatedUuid[8], '-'); + ASSERT_EQ(generatedUuid[13], '-'); + ASSERT_EQ(generatedUuid[14], '4'); + ASSERT_EQ(generatedUuid[18], '-'); + ASSERT_EQ(generatedUuid[23], '-'); +} + + +TEST_F(StringTest, shouldGenerateUuid4Default) { const auto generatedUuid = uuid(); From 525a75534a9d9b245b1b44a89c82e25db4f035be Mon Sep 17 00:00:00 2001 From: Blakely Mayhall <120978220+blakelymayhall@users.noreply.github.com> Date: Sun, 1 Dec 2024 02:17:28 -0600 Subject: [PATCH 4/7] fix: improve geodetic coordinate generation and comparison (#1006) * switch to vincenty method for geodetic calculations * remove debug statements --- src/modules/location.cpp | 56 +++++++++++++++++++++++---- tests/modules/location_test.cpp | 67 ++++++++++++++++++++++++++++++++- 2 files changed, 113 insertions(+), 10 deletions(-) diff --git a/src/modules/location.cpp b/src/modules/location.cpp index 0861c786c..82271f5e4 100644 --- a/src/modules/location.cpp +++ b/src/modules/location.cpp @@ -255,6 +255,9 @@ std::string_view timeZone() std::tuple nearbyGPSCoordinate(Precision precision, const std::tuple& origin, const double radius, const bool isMetric) { + auto toRadians = [](double degree) -> double { return degree * M_PI / 180.0; }; + auto toDegrees = [](double radian) -> double { return radian * 180.0 / M_PI; }; + // If origin is not provided, generate a random GPS coordinate. if (std::get<0>(origin) == std::numeric_limits::max() && std::get<1>(origin) == std::numeric_limits::max()) @@ -262,21 +265,58 @@ std::tuple nearbyGPSCoordinate(Precision precision, co return {latitude(precision), longitude(precision)}; } + double lat1 = toRadians(std::get<0>(origin)); + double lon1 = toRadians(std::get<1>(origin)); + const auto angleRadians = number::decimal(2 * M_PI); + double sinAlpha1 = sin(angleRadians); + double cosAlpha1 = cos(angleRadians); const auto radiusMetric = isMetric ? radius : radius * 1.60934; const auto distanceInKm = number::decimal(radiusMetric); - constexpr auto kmPerDegreeLatitude = 110.574; // The distance in km per degree for earth's latitude. - const auto kmPerDegreeLongitude = - 111.320 * - std::cos(std::get<0>(origin) / 180 * M_PI); // The distance in km per degree for a specific longitude in earth. + // Compute new lat,lon pair via Vincenty Direct Method + constexpr double WGS84_A = 6378137.0; // Semi-major axis (meters) + constexpr double WGS84_F = 1 / 298.257223563; // Flattening + constexpr double WGS84_B = WGS84_A * (1 - WGS84_F); // Semi-minor axis (meters) + + double U1 = atan((1 - WGS84_F) * tan(lat1)); + double sinU1 = sin(U1), cosU1 = cos(U1); - const auto distanceInDegreeLatitude = distanceInKm / kmPerDegreeLatitude; - const auto distanceInDegreeLongitude = distanceInKm / kmPerDegreeLongitude; + double sigma1 = atan2(tan(U1), cosAlpha1); + double sinAlpha = cosU1 * sinAlpha1; + double cos2Alpha = 1 - sinAlpha * sinAlpha; - auto coordinateLatitude = std::get<0>(origin) + distanceInDegreeLatitude * std::sin(angleRadians); - auto coordinateLongitude = std::get<1>(origin) + distanceInDegreeLongitude * std::cos(angleRadians); + double uSquared = cos2Alpha * (WGS84_A * WGS84_A - WGS84_B * WGS84_B) / (WGS84_B * WGS84_B); + double A = 1 + (uSquared / 16384.0) * (4096.0 + uSquared * (-768.0 + uSquared * (320.0 - 175.0 * uSquared))); + double B = (uSquared / 1024.0) * (256.0 + uSquared * (-128.0 + uSquared * (74.0 - 47.0 * uSquared))); + + double sigma = distanceInKm * 1000.0 / (WGS84_B * A); + double sigmaP; + double cos2SigmaM, sinSigma, cosSigma, deltaSigma; + do + { + sigmaP = sigma; + cos2SigmaM = cos(2 * sigma1 + sigma); + sinSigma = sin(sigma); + cosSigma = cos(sigma); + deltaSigma = B * sinSigma * + (cos2SigmaM + (B / 4.0) * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - + (B / 6.0) * cos2SigmaM * (-3 + 4 * sinSigma * sinSigma) * + (-3 + 4 * cos2SigmaM * cos2SigmaM))); + sigma = distanceInKm * 1000.0 / (WGS84_B * A) + deltaSigma; + } while (fabs(sigma - sigmaP) > 1e-12); + + double tmp = sinU1 * sinSigma - cosU1 * cosSigma * cosAlpha1; + double lat2 = + atan2(sinU1 * cosSigma + cosU1 * sinSigma * cosAlpha1, (1 - WGS84_F) * sqrt(sinAlpha * sinAlpha + tmp * tmp)); + double lambda = atan2(sinSigma * sinAlpha1, cosU1 * cosSigma - sinU1 * sinSigma * cosAlpha1); + double C = (WGS84_F / 16.0) * cos2Alpha * (4.0 + WGS84_F * (4.0 - 3.0 * cos2Alpha)); + double L = lambda - (1 - C) * WGS84_F * sinAlpha * + (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM))); + + double coordinateLatitude = toDegrees(lat2); + double coordinateLongitude = toDegrees(lon1 + L); // Box the latitude [-90, 90] coordinateLatitude = std::fmod(coordinateLatitude, 180.0); diff --git a/tests/modules/location_test.cpp b/tests/modules/location_test.cpp index a0aaf4a09..9c94d9b89 100644 --- a/tests/modules/location_test.cpp +++ b/tests/modules/location_test.cpp @@ -152,6 +152,68 @@ class LocationTest : public TestWithParam std::pow(std::sin(dLat / 2), 2) + std::pow(std::sin(dLon / 2), 2) * std::cos(lat1) * std::cos(lat2); return 2 * EARTH_RADIUS_KM * std::atan2(std::sqrt(a), std::sqrt(1 - a)); } + + static constexpr double WGS84_A = 6378137.0; + static constexpr double WGS84_F = 1 / 298.257223563; + static constexpr double WGS84_B = WGS84_A * (1 - WGS84_F); + + static double vincentyDistance(double lat1, double lon1, double lat2, double lon2) + { + auto toRadians = [](double degree) -> double { return degree * M_PI / 180.0; }; + constexpr double TOLERANCE = 1e-12; + constexpr int MAX_ITERATIONS = 1000; + + double dLon = toRadians(lon2 - lon1); + + double U1 = atan((1 - WGS84_F) * tan(toRadians(lat1))); + double U2 = atan((1 - WGS84_F) * tan(toRadians(lat2))); + + double Lambda = dLon; + double LambdaPrev; + double sinSigma, cosSigma, sigma, sinAlpha, cos2Alpha, cos2SigmaM; + int iterations = 0; + do + { + LambdaPrev = Lambda; + + double sinLambda = sin(Lambda); + double cosLambda = cos(Lambda); + + double sinU1 = sin(U1), cosU1 = cos(U1); + double sinU2 = sin(U2), cosU2 = cos(U2); + sinSigma = sqrt(pow(cosU2 * sinLambda, 2) + pow(cosU1 * sinU2 - sinU1 * cosU2 * cosLambda, 2)); + if (sinSigma == 0) + { + return 0.0; // Points are coincident + } + + cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda; + sigma = atan2(sinSigma, cosSigma); + sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma; + cos2Alpha = 1 - sinAlpha * sinAlpha; + cos2SigmaM = (cos2Alpha != 0) ? cosSigma - 2 * sinU1 * sinU2 / cos2Alpha : 0.0; + double C = (WGS84_F / 16) * cos2Alpha * (4 + WGS84_F * (4 - 3 * cos2Alpha)); + Lambda = + dLon + (1 - C) * WGS84_F * sinAlpha * + (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM))); + iterations++; + } while (fabs(Lambda - LambdaPrev) > TOLERANCE && iterations < MAX_ITERATIONS); + + if (iterations >= MAX_ITERATIONS) + { + // Vincenty algorithm failed to converge -- fail the test + return 1e9; + } + + double uSquared = cos2Alpha * (WGS84_A * WGS84_A - WGS84_B * WGS84_B) / (WGS84_B * WGS84_B); + double A = 1 + (uSquared / 16384) * (4096 + uSquared * (-768 + uSquared * (320 - 175 * uSquared))); + double B = (uSquared / 1024) * (256 + uSquared * (-128 + uSquared * (74 - 47 * uSquared))); + double deltaSigma = B * sinSigma * + (cos2SigmaM + (B / 4) * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - + (B / 6) * cos2SigmaM * (-3 + 4 * sinSigma * sinSigma) * + (-3 + 4 * cos2SigmaM * cos2SigmaM))); + return WGS84_B * A * (sigma - deltaSigma) / 1000.0; + } }; TEST_P(LocationTest, shouldGenerateState) @@ -582,7 +644,7 @@ TEST_F(LocationTest, shouldGenerateNearbyGPSCoordinateWithOriginInKilometers) ASSERT_EQ(generatedLongitudeParts.size(), 2); ASSERT_EQ(generatedLongitudeParts[1].size(), 3); - const auto distance = haversine(std::get<0>(origin), std::get<1>(origin), latitudeAsFloat, longitudeAsFloat); + const auto distance = vincentyDistance(std::get<0>(origin), std::get<1>(origin), latitudeAsFloat, longitudeAsFloat); ASSERT_LE(distance, 10.0); } @@ -606,7 +668,8 @@ TEST_F(LocationTest, shouldGenerateNearbyGPSCoordinateWithOriginInMiles) ASSERT_EQ(generatedLongitudeParts.size(), 2); ASSERT_EQ(generatedLongitudeParts[1].size(), 3); - const auto distanceKm = haversine(std::get<0>(origin), std::get<1>(origin), latitudeAsFloat, longitudeAsFloat); + const auto distanceKm = + vincentyDistance(std::get<0>(origin), std::get<1>(origin), latitudeAsFloat, longitudeAsFloat); const auto distanceMiles = distanceKm * 0.621371; ASSERT_LE(distanceMiles, 10.0); From 96e0263cc8b9e9f7a624b0568cbf7fba2e0fd7e9 Mon Sep 17 00:00:00 2001 From: Sumeet Khedkar Date: Mon, 2 Dec 2024 09:45:53 -0800 Subject: [PATCH 5/7] feat: Added locale for movies (#1000) * Added module constructor to both header (defination and the declaration * Reverting the changes as they are directly pushed into main * Added constructor for en-us locale * Implemented en_us for movies * Fixed tests and removed faker submodule * Instantiated google verification in Movie module * Fixed build issues * Fixed comment for the doxygen --- .gitmodules | 1 - CMakePresets.json | 21 +++++++++- externals/googletest | 2 +- include/faker-cxx/movie.h | 26 ++++++++---- src/modules/movie.cpp | 48 ++++++++++++++++------ src/modules/movie_data.h | 35 ++++++++++++---- tests/modules/movie_test.cpp | 79 +++++++++++++++++++++++++++--------- 7 files changed, 164 insertions(+), 48 deletions(-) diff --git a/.gitmodules b/.gitmodules index 8d680e1b8..0222bacd1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,4 +4,3 @@ [submodule "externals/fmt"] path = externals/fmt url = https://github.com/fmtlib/fmt.git - diff --git a/CMakePresets.json b/CMakePresets.json index cc5a38e32..a66af6338 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -199,6 +199,25 @@ "CMAKE_EXPORT_COMPILE_COMMANDS": "ON", "BUILD_SHARED_LIBS": "ON" } + }, + { + "name": "Enter\\", + "description": "", + "displayName": "", + "inherits": [ + "unixlike-gcc-debug-static" + ] + }, + { + "name": "Enter", + "description": "", + "displayName": "", + "inherits": [ + "unixlike-gcc-debug-static", + "unixlike-gcc-debug-shared", + "unixlike-gcc-release-static", + "unixlike-gcc-release-shared" + ] } ], "buildPresets": [ @@ -274,7 +293,7 @@ "configurePreset": "windows-msvc-debug-shared", "configuration": "Debug" } - ], + ], "testPresets": [ { "name": "test-common", diff --git a/externals/googletest b/externals/googletest index 5ed218639..35d0c3656 160000 --- a/externals/googletest +++ b/externals/googletest @@ -1 +1 @@ -Subproject commit 5ed21863955149a5a877a53d7d5045b6919090ed +Subproject commit 35d0c365609296fa4730d62057c487e3cfa030ff diff --git a/include/faker-cxx/movie.h b/include/faker-cxx/movie.h index dce7e6652..4a272f03f 100644 --- a/include/faker-cxx/movie.h +++ b/include/faker-cxx/movie.h @@ -3,11 +3,14 @@ #include #include "faker-cxx/export.h" +#include "faker-cxx/types/locale.h" namespace faker::movie { /** - * @brief Returns a random movie genre. + * @brief Returns a random movie genre + * + * @param locale The locale. Defaults to `Locale::en_US`. * * @returns Movie genre. * @@ -15,9 +18,11 @@ namespace faker::movie * faker::movie::genre() // "Drama" * @endcode */ -FAKER_CXX_EXPORT std::string_view genre(); +FAKER_CXX_EXPORT std::string_view genre(Locale locale = Locale::en_US); /** + * @param locale The locale. Defaults to `Locale::en_US`. + * * @brief Returns a random movie title. * * @returns Movie title. @@ -26,9 +31,10 @@ FAKER_CXX_EXPORT std::string_view genre(); * faker::movie::movieTitle() // "Pulp Fiction" * @endcode */ -FAKER_CXX_EXPORT std::string_view movieTitle(); +FAKER_CXX_EXPORT std::string_view movieTitle(Locale locale = Locale::en_US); /** + * @param locale The locale. Defaults to `Locale::en_US`. * @brief Returns a random tv show. * * @returns Tv show. @@ -37,9 +43,11 @@ FAKER_CXX_EXPORT std::string_view movieTitle(); * faker::movie::tvShow() // "The Sopranos" * @endcode */ -FAKER_CXX_EXPORT std::string_view tvShow(); +FAKER_CXX_EXPORT std::string_view tvShow(Locale locale = Locale::en_US); /** + * @param locale The locale. Defaults to `Locale::en_US`. + * * @brief Returns a random movie director name. * * @returns Movie director name. @@ -48,9 +56,11 @@ FAKER_CXX_EXPORT std::string_view tvShow(); * faker::movie::director() // "Quentin Tarantino" * @endcode */ -FAKER_CXX_EXPORT std::string_view director(); +FAKER_CXX_EXPORT std::string_view director(Locale locale = Locale::en_US); /** + * @param locale The locale. Defaults to `Locale::en_US`. + * * @brief Returns a random actor name. * * @returns Actor name. @@ -59,9 +69,11 @@ FAKER_CXX_EXPORT std::string_view director(); * faker::movie::actor() // "John Travolta" * @endcode */ -FAKER_CXX_EXPORT std::string_view actor(); +FAKER_CXX_EXPORT std::string_view actor(Locale locale = Locale::en_US); /** + * @param locale The locale. Defaults to `Locale::en_US`. + * * @brief Returns a random actress name. * * @returns Actress name. @@ -70,5 +82,5 @@ FAKER_CXX_EXPORT std::string_view actor(); * faker::movie::actress() // "Scarlett Johansson" * @endcode */ -FAKER_CXX_EXPORT std::string_view actress(); +FAKER_CXX_EXPORT std::string_view actress(Locale locale = Locale::en_US); } diff --git a/src/modules/movie.cpp b/src/modules/movie.cpp index fde2841ac..09a4b9068 100644 --- a/src/modules/movie.cpp +++ b/src/modules/movie.cpp @@ -7,34 +7,58 @@ namespace faker::movie { -std::string_view genre() +namespace { - return helper::randomElement(movieGenres); +const struct MovieDefinition& getMovie(Locale locale) +{ + switch (locale) + { + default: + return enUSmoviesDefinitions; + } +} } -std::string_view movieTitle() +std::string_view genre(Locale locale) { - return helper::randomElement(movies); + const auto& movie = getMovie(locale); + + return helper::randomElement(movie.genres); } -std::string_view tvShow() +std::string_view movieTitle(Locale locale) { - return helper::randomElement(tvShows); + const auto& movie = getMovie(locale); + + return helper::randomElement(movie.movies); } -std::string_view director() +std::string_view tvShow(Locale locale) { - return helper::randomElement(directors); + const auto& movie = getMovie(locale); + + return helper::randomElement(movie.tvShows); } -std::string_view actor() +std::string_view director(Locale locale) { - return helper::randomElement(actors); + const auto& movie = getMovie(locale); + + return helper::randomElement(movie.directors); } -std::string_view actress() +std::string_view actor(Locale locale) { - return helper::randomElement(actresses); + const auto& movie = getMovie(locale); + + return helper::randomElement(movie.actors); } +std::string_view actress(Locale locale) +{ + const auto& movie = getMovie(locale); + + return helper::randomElement(movie.actresses); } + +} \ No newline at end of file diff --git a/src/modules/movie_data.h b/src/modules/movie_data.h index 74a4f9e1a..9cd7d0332 100644 --- a/src/modules/movie_data.h +++ b/src/modules/movie_data.h @@ -1,13 +1,26 @@ #pragma once #include +#include #include #include "faker-cxx/movie.h" namespace faker::movie { -const auto actors = std::to_array({ + +struct MovieDefinition +{ + std::span actors; + std::span actresses; + std::span directors; + std::span genres; + std::span movies; + std::span tvShows; +}; + +// "en-us" +const auto enUSactors = std::to_array({ "Aamir Khan", "Abbott and Costello", "Adel Imam", @@ -309,7 +322,7 @@ const auto actors = std::to_array({ "Yves Montand", }); -const auto actresses = std::to_array({ +const auto enUSactresses = std::to_array({ "Agnes Moorehead", "Aishwarya Rai", "Alida Valli", @@ -612,7 +625,7 @@ const auto actresses = std::to_array({ "Zhang Ziyi", }); -const auto directors = std::to_array({ +const auto enUSdirectors = std::to_array({ "Abbas Kiarostami", "Abel Gance", "Agnès Varda", @@ -915,10 +928,10 @@ const auto directors = std::to_array({ "Éric Rohmer", }); -const auto movieGenres = std::to_array( +const auto enUSmovieGenres = std::to_array( {"Action", "Comedy", "Drama", "Fantasy", "Horror", "Mystery", "Romance", "Thriller", "Western"}); -const auto movies = std::to_array({ +const auto enUSmovies = std::to_array({ "12 Angry Men", "2001: A Space Odyssey", "8½", @@ -1171,7 +1184,7 @@ const auto movies = std::to_array({ "Yôjinbô", }); -const auto tvShows = std::to_array({ +const auto enUStvShows = std::to_array({ "Africa", "Apocalypse: The Second World War", "Arcane", @@ -1296,4 +1309,12 @@ const auto tvShows = std::to_array({ "When They See Us", }); -} +const MovieDefinition enUSmoviesDefinitions = { + .actors = enUSactors, + .actresses = enUSactresses, + .directors = enUSdirectors, + .genres = enUSmovieGenres, + .movies = enUSmovies, + .tvShows = enUStvShows, +}; +} \ No newline at end of file diff --git a/tests/modules/movie_test.cpp b/tests/modules/movie_test.cpp index b1533db67..73b28dfcf 100644 --- a/tests/modules/movie_test.cpp +++ b/tests/modules/movie_test.cpp @@ -7,57 +7,98 @@ #include "movie_data.h" using namespace ::testing; +using namespace faker; using namespace faker::movie; -class MovieTest : public Test +namespace +{ +const struct MovieDefinition& getMovie(Locale locale) +{ + switch (locale) + { + default: + return enUSmoviesDefinitions; + } +} +} + +class MovieTest : public TestWithParam { public: }; -TEST_F(MovieTest, shouldGenerateGenre) +TEST_P(MovieTest, shouldGenerateGenreLocale) { - const auto generatedGenre = genre(); + const auto locale = GetParam(); - ASSERT_TRUE(std::ranges::any_of(movieGenres, [generatedGenre](const std::string_view& genre) + const auto& movie = getMovie(locale); + + const auto& generatedGenre = genre(locale); + + ASSERT_TRUE(std::ranges::any_of(movie.genres, [generatedGenre](const std::string_view& genre) { return generatedGenre == genre; })); } -TEST_F(MovieTest, shouldGenerateMovieTitle) +TEST_P(MovieTest, shouldGenerateMovieTitleLocale) { - const auto generatedMovieTitle = movieTitle(); + const auto locale = GetParam(); + + const auto& movie = getMovie(locale); - ASSERT_TRUE(std::ranges::any_of(movies, [generatedMovieTitle](const std::string_view& movieTitle) + const auto generatedMovieTitle = movieTitle(locale); + + ASSERT_TRUE(std::ranges::any_of(movie.movies, [generatedMovieTitle](const std::string_view& movieTitle) { return generatedMovieTitle == movieTitle; })); } -TEST_F(MovieTest, shouldGenerateTvShow) +TEST_P(MovieTest, shouldGenerateTvShowLocale) { - const auto generatedTvShow = tvShow(); + const auto locale = GetParam(); + + const auto& movie = getMovie(locale); - ASSERT_TRUE(std::ranges::any_of(tvShows, [generatedTvShow](const std::string_view& tvShow) + const auto generatedTvShow = tvShow(locale); + + ASSERT_TRUE(std::ranges::any_of(movie.tvShows, [generatedTvShow](const std::string_view& tvShow) { return generatedTvShow == tvShow; })); } -TEST_F(MovieTest, shouldGenerateDirector) +TEST_P(MovieTest, shouldGenerateDirectorLocale) { - const auto generatedDirector = director(); + const auto locale = GetParam(); + + const auto& movie = getMovie(locale); + + const auto generatedDirector = director(locale); - ASSERT_TRUE(std::ranges::any_of(directors, [generatedDirector](const std::string_view& director) + ASSERT_TRUE(std::ranges::any_of(movie.directors, [generatedDirector](const std::string_view& director) { return generatedDirector == director; })); } -TEST_F(MovieTest, shouldGenerateActor) +TEST_P(MovieTest, shouldGenerateActorLocale) { - const auto generatedActor = actor(); + const auto locale = GetParam(); + + const auto& movie = getMovie(locale); + + const auto generatedActor = actor(locale); - ASSERT_TRUE(std::ranges::any_of(actors, [generatedActor](const std::string_view& actor) + ASSERT_TRUE(std::ranges::any_of(movie.actors, [generatedActor](const std::string_view& actor) { return generatedActor == actor; })); } -TEST_F(MovieTest, shouldGenerateActress) +TEST_P(MovieTest, shouldGenerateActressLocale) { - const auto generatedActress = actress(); + const auto locale = GetParam(); - ASSERT_TRUE(std::ranges::any_of(actresses, [generatedActress](const std::string_view& actress) + const auto& movie = getMovie(locale); + + const auto generatedActress = actress(locale); + + ASSERT_TRUE(std::ranges::any_of(movie.actresses, [generatedActress](const std::string_view& actress) { return generatedActress == actress; })); } + +INSTANTIATE_TEST_SUITE_P(TestMovieByLocale, MovieTest, ValuesIn(locales), + [](const TestParamInfo& paramInfo) { return toString(paramInfo.param); }); + From 4bb709854396aab36bc53983267adf9470f2328b Mon Sep 17 00:00:00 2001 From: Sumeet Khedkar Date: Mon, 2 Dec 2024 10:05:27 -0800 Subject: [PATCH 6/7] feat: Feature/science locale (#1001) * Added module constructor to both header (defination and the declaration * Reverting the changes as they are directly pushed into main * Added locale for science module(en_US) * fixed the formatting using clang-format * Added comments to the new parameters * Fixed the issue with formatting. --- include/faker-cxx/science.h | 29 ++++++++--- src/modules/science.cpp | 63 +++++++++++++++------- src/modules/science_data.h | 32 +++++++++--- tests/modules/science_test.cpp | 95 ++++++++++++++++++++++++---------- 4 files changed, 161 insertions(+), 58 deletions(-) diff --git a/include/faker-cxx/science.h b/include/faker-cxx/science.h index 9b1596336..a044f1907 100644 --- a/include/faker-cxx/science.h +++ b/include/faker-cxx/science.h @@ -3,6 +3,7 @@ #include #include "faker-cxx/export.h" +#include "faker-cxx/types/locale.h" namespace faker::science { @@ -16,6 +17,8 @@ struct FAKER_CXX_EXPORT ChemicalElement /** * @brief Returns a random chemical element from the periodic table. * + * @param locale The locale. Defaults to `Locale::en_US`. + * * @returns Chemical element. * * @code @@ -23,7 +26,7 @@ struct FAKER_CXX_EXPORT ChemicalElement * table. * @endcode */ -FAKER_CXX_EXPORT ChemicalElement chemicalElement(); +FAKER_CXX_EXPORT ChemicalElement chemicalElement(Locale locale = Locale::en_US); struct FAKER_CXX_EXPORT Unit { @@ -35,17 +38,21 @@ struct FAKER_CXX_EXPORT Unit /** * @brief Returns a unit of measurement for either distance, mass, time, temp, current. * + * @param locale The locale. Defaults to `Locale::en_US`. + * * @returns Unit. * * @code * faker::science::unit() // Object of Unit containing info about a random unit of measurement. * @endcode */ -FAKER_CXX_EXPORT Unit unit(); +FAKER_CXX_EXPORT Unit unit(Locale locale = Locale::en_US); /** * @brief Returns a unit of measurement for either distance. * + * @param locale The locale. Defaults to `Locale::en_US`. + * * @returns Distance unit. * * @code @@ -53,44 +60,52 @@ FAKER_CXX_EXPORT Unit unit(); * distance. * @endcode */ -FAKER_CXX_EXPORT Unit distanceUnit(); +FAKER_CXX_EXPORT Unit distanceUnit(Locale locale = Locale::en_US); /** * @brief Returns a unit of measurement for either time. * + * @param locale The locale. Defaults to `Locale::en_US`. + * * @returns Time unit. * * @code * faker::science::timeUnit() // Object of Unit containing info about a random unit of measurement used to measure time. * @endcode */ -FAKER_CXX_EXPORT Unit timeUnit(); +FAKER_CXX_EXPORT Unit timeUnit(Locale locale = Locale::en_US); /** * @brief Returns a unit of measurement for either mass. * + * @param locale The locale. Defaults to `Locale::en_US`. + * * @returns Mass unit. * * @code * faker::science::massUnit() // Object of Unit containing info about a random unit of measurement used to measure mass. * @endcode */ -FAKER_CXX_EXPORT Unit massUnit(); +FAKER_CXX_EXPORT Unit massUnit(Locale locale = Locale::en_US); /** * @brief Returns a unit of measurement for either temp. * + * @param locale The locale. Defaults to `Locale::en_US`. + * * @returns Temperture unit. * * @code * faker::science::tempUnit() // Object of Unit containing info about a random unit of measurement used to measure temp. * @endcode */ -FAKER_CXX_EXPORT Unit tempUnit(); +FAKER_CXX_EXPORT Unit tempUnit(Locale locale = Locale::en_US); /** * @brief Returns a unit of measurement for either current. * + * @param locale The locale. Defaults to `Locale::en_US`. + * * @returns Current unit. * * @code @@ -98,5 +113,5 @@ FAKER_CXX_EXPORT Unit tempUnit(); * current. * @endcode */ -FAKER_CXX_EXPORT Unit currentUnit(); +FAKER_CXX_EXPORT Unit currentUnit(Locale locale = Locale::en_US); } diff --git a/src/modules/science.cpp b/src/modules/science.cpp index 2ba902831..650a0838c 100644 --- a/src/modules/science.cpp +++ b/src/modules/science.cpp @@ -5,47 +5,72 @@ namespace faker::science { -ChemicalElement chemicalElement() +namespace { - return helper::randomElement(chemicalElements); +const struct ScienceDefinition& getScienceDefinition(Locale locale) +{ + switch (locale) + { + default: + return enUSscienceDefinition; + } +} } -Unit unit() +ChemicalElement chemicalElement(Locale locale) { + const auto& scienceDefinition = getScienceDefinition(locale); + + return helper::randomElement(scienceDefinition.chemicalElements); +} + +Unit unit(Locale locale) +{ + const auto& scienceDefinition = getScienceDefinition(locale); + std::vector units; - units.insert(units.end(), distanceUnits.begin(), distanceUnits.end()); - units.insert(units.end(), massUnits.begin(), massUnits.end()); - units.insert(units.end(), timeUnits.begin(), timeUnits.end()); - units.insert(units.end(), currentUnits.begin(), currentUnits.end()); - units.insert(units.end(), temperatureUnits.begin(), temperatureUnits.end()); + units.insert(units.end(), scienceDefinition.distanceUnits.begin(), scienceDefinition.distanceUnits.end()); + units.insert(units.end(), scienceDefinition.massUnits.begin(), scienceDefinition.massUnits.end()); + units.insert(units.end(), scienceDefinition.timeUnits.begin(), scienceDefinition.timeUnits.end()); + units.insert(units.end(), scienceDefinition.currentUnits.begin(), scienceDefinition.currentUnits.end()); + units.insert(units.end(), scienceDefinition.temperatureUnits.begin(), scienceDefinition.temperatureUnits.end()); return helper::randomElement(units); } -Unit distanceUnit() +Unit distanceUnit(Locale locale) { - return helper::randomElement(distanceUnits); + const auto& scienceDefinition = getScienceDefinition(locale); + + return helper::randomElement(scienceDefinition.distanceUnits); } -Unit timeUnit() +Unit timeUnit(Locale locale) { - return helper::randomElement(timeUnits); + const auto& scienceDefinition = getScienceDefinition(locale); + + return helper::randomElement(scienceDefinition.timeUnits); } -Unit massUnit() +Unit massUnit(Locale locale) { - return helper::randomElement(massUnits); + const auto& scienceDefinition = getScienceDefinition(locale); + + return helper::randomElement(scienceDefinition.massUnits); } -Unit tempUnit() +Unit tempUnit(Locale locale) { - return helper::randomElement(temperatureUnits); + const auto& scienceDefinition = getScienceDefinition(locale); + + return helper::randomElement(scienceDefinition.temperatureUnits); } -Unit currentUnit() +Unit currentUnit(Locale locale) { - return helper::randomElement(currentUnits); -} + const auto& scienceDefinition = getScienceDefinition(locale); + return helper::randomElement(scienceDefinition.currentUnits); +} } diff --git a/src/modules/science_data.h b/src/modules/science_data.h index c7c1b9ef7..98f584c4a 100644 --- a/src/modules/science_data.h +++ b/src/modules/science_data.h @@ -1,12 +1,23 @@ #pragma once #include +#include #include "faker-cxx/science.h" namespace faker::science { -const auto chemicalElements = std::to_array( +struct ScienceDefinition +{ + std::span chemicalElements; + std::span distanceUnits; + std::span massUnits; + std::span timeUnits; + std::span currentUnits; + std::span temperatureUnits; +}; + +const auto enUSchemicalElements = std::to_array( {{"Hydrogen", "H", 1}, {"Helium", "He", 2}, {"Lithium", "Li", 3}, {"Beryllium", "Be", 4}, {"Boron", "B", 5}, {"Carbon", "C", 6}, {"Nitrogen", "N", 7}, {"Oxygen", "O", 8}, {"Fluorine", "F", 9}, {"Neon", "Ne", 10}, {"Sodium", "Na", 11}, {"Magnesium", "Mg", 12}, @@ -38,7 +49,7 @@ const auto chemicalElements = std::to_array( {"Nihonium", "Nh", 113}, {"Flerovium", "Fl", 114}, {"Moscovium", "Mc", 115}, {"Livermorium", "Lv", 116}, {"Tennessine", "Ts", 117}, {"Oganesson", "Og", 118}}); -const auto distanceUnits = std::to_array({ +const auto enUSdistanceUnits = std::to_array({ {"Millimeter", "mm", "Length"}, {"Centimeter", "cm", "Length"}, {"Meter", "m", "Length"}, @@ -49,7 +60,7 @@ const auto distanceUnits = std::to_array({ {"Mile", "mi", "Length"}, }); -const auto massUnits = std::to_array({ +const auto enUSmassUnits = std::to_array({ {"Gram", "g", "Mass"}, {"Kilogram", "kg", "Mass"}, {"Milligram", "mg", "Mass"}, @@ -60,7 +71,7 @@ const auto massUnits = std::to_array({ {"Slug", "sl", "Mass"}, }); -const auto timeUnits = std::to_array({ +const auto enUStimeUnits = std::to_array({ {"Second", "s", "Time"}, {"Minute", "min", "Time"}, {"Hour", "hr", "Time"}, @@ -70,16 +81,25 @@ const auto timeUnits = std::to_array({ {"Year", "yr", "Time"}, }); -const auto currentUnits = std::to_array({ +const auto enUScurrentUnits = std::to_array({ {"Ampere", "A", "Electric Current"}, {"Milliampere", "mA", "Electric Current"}, {"Microampere", "μA", "Electric Current"}, }); -const auto temperatureUnits = std::to_array({ +const auto enUStemperatureUnits = std::to_array({ {"Celcius", "°C", "Temperature"}, {"Fahrenheit", "°F", "Temperature"}, {"Kelvin", "K", "Temperature"}, }); +const ScienceDefinition enUSscienceDefinition = { + .chemicalElements = enUSchemicalElements, + .distanceUnits = enUSdistanceUnits, + .massUnits = enUSmassUnits, + .timeUnits = enUStimeUnits, + .currentUnits = enUScurrentUnits, + .temperatureUnits = enUStemperatureUnits, +}; + } diff --git a/tests/modules/science_test.cpp b/tests/modules/science_test.cpp index 2fd1ddca4..90d86684e 100644 --- a/tests/modules/science_test.cpp +++ b/tests/modules/science_test.cpp @@ -8,9 +8,21 @@ using namespace ::testing; using namespace faker; -using namespace science; +using namespace faker::science; -class ScienceTest : public Test +namespace +{ +const struct ScienceDefinition& getScienceDefinition(Locale locale) +{ + switch (locale) + { + default: + return enUSscienceDefinition; + } +} +} + +class ScienceTest : public TestWithParam { public: static bool chemicalElementsAreEqual(const ChemicalElement& chemElement1, const ChemicalElement& chemElement2) @@ -25,65 +37,96 @@ class ScienceTest : public Test } }; -TEST_F(ScienceTest, shouldGenerateChemElement) +TEST_P(ScienceTest, shouldGenerateChemElement) { - const auto generatedChemElement = chemicalElement(); + const auto locale = GetParam(); + + const auto& scienceDefinition = getScienceDefinition(locale); + + const auto generatedChemElement = chemicalElement(locale); - ASSERT_TRUE(std::ranges::any_of(chemicalElements, [generatedChemElement](const ChemicalElement& chemElement) + ASSERT_TRUE(std::ranges::any_of(scienceDefinition.chemicalElements, [generatedChemElement](const ChemicalElement& chemElement) { return chemicalElementsAreEqual(generatedChemElement, chemElement); })); } -TEST_F(ScienceTest, shouldGenerateAnyUnit) +TEST_P(ScienceTest, shouldGenerateAnyUnit) { + const auto locale = GetParam(); + + const auto& scienceDefinition = getScienceDefinition(locale); + std::vector units; - units.insert(units.end(), distanceUnits.begin(), distanceUnits.end()); - units.insert(units.end(), massUnits.begin(), massUnits.end()); - units.insert(units.end(), timeUnits.begin(), timeUnits.end()); - units.insert(units.end(), currentUnits.begin(), currentUnits.end()); - units.insert(units.end(), temperatureUnits.begin(), temperatureUnits.end()); + units.insert(units.end(), scienceDefinition.distanceUnits.begin(), scienceDefinition.distanceUnits.end()); + units.insert(units.end(), scienceDefinition.massUnits.begin(), scienceDefinition.massUnits.end()); + units.insert(units.end(), scienceDefinition.timeUnits.begin(), scienceDefinition.timeUnits.end()); + units.insert(units.end(), scienceDefinition.currentUnits.begin(), scienceDefinition.currentUnits.end()); + units.insert(units.end(), scienceDefinition.temperatureUnits.begin(), scienceDefinition.temperatureUnits.end()); - const auto generatedAnyUnit = unit(); + const auto generatedAnyUnit = unit(locale); ASSERT_TRUE(std::ranges::any_of(units, [generatedAnyUnit](const Unit& unit) { return unitsAreEqual(generatedAnyUnit, unit); })); } -TEST_F(ScienceTest, shouldGenerateDistanceUnit) +TEST_P(ScienceTest, shouldGenerateDistanceUnit) { - const auto generatedDistanceUnit = distanceUnit(); + const auto locale = GetParam(); - ASSERT_TRUE(std::ranges::any_of(distanceUnits, [generatedDistanceUnit](const Unit& distanceUnit) + const auto& scienceDefinition = getScienceDefinition(locale); + + const auto generatedDistanceUnit = distanceUnit(locale); + + ASSERT_TRUE(std::ranges::any_of(scienceDefinition.distanceUnits, [generatedDistanceUnit](const Unit& distanceUnit) { return unitsAreEqual(generatedDistanceUnit, distanceUnit); })); } -TEST_F(ScienceTest, shouldGenerateMassUnit) +TEST_P(ScienceTest, shouldGenerateMassUnit) { - const auto generatedMassUnit = massUnit(); + const auto locale = GetParam(); + + const auto& scienceDefinition = getScienceDefinition(locale); - ASSERT_TRUE(std::ranges::any_of(massUnits, [generatedMassUnit](const Unit& massUnit) + const auto generatedMassUnit = massUnit(locale); + + ASSERT_TRUE(std::ranges::any_of(scienceDefinition.massUnits, [generatedMassUnit](const Unit& massUnit) { return unitsAreEqual(generatedMassUnit, massUnit); })); } -TEST_F(ScienceTest, shouldGenerateTimeUnit) +TEST_P(ScienceTest, shouldGenerateTimeUnit) { - const auto generatedTimeUnit = timeUnit(); + const auto locale = GetParam(); + + const auto& scienceDefinition = getScienceDefinition(locale); + + const auto generatedTimeUnit = timeUnit(locale); - ASSERT_TRUE(std::ranges::any_of(timeUnits, [generatedTimeUnit](const Unit& timeUnit) + ASSERT_TRUE(std::ranges::any_of(scienceDefinition.timeUnits, [generatedTimeUnit](const Unit& timeUnit) { return unitsAreEqual(generatedTimeUnit, timeUnit); })); } -TEST_F(ScienceTest, shouldGenerateTempUnit) +TEST_P(ScienceTest, shouldGenerateTempUnit) { - const auto generatedTempUnit = tempUnit(); + const auto locale = GetParam(); - ASSERT_TRUE(std::ranges::any_of(temperatureUnits, [generatedTempUnit](const Unit& tempUnit) + const auto& scienceDefinition = getScienceDefinition(locale); + + const auto generatedTempUnit = tempUnit(locale); + + ASSERT_TRUE(std::ranges::any_of(scienceDefinition.temperatureUnits, [generatedTempUnit](const Unit& tempUnit) { return unitsAreEqual(generatedTempUnit, tempUnit); })); } -TEST_F(ScienceTest, shouldGenerateCurrentUnit) +TEST_P(ScienceTest, shouldGenerateCurrentUnit) { + const auto locale = GetParam(); + + const auto& scienceDefinition = getScienceDefinition(locale); + const auto generatedCurrentUnit = currentUnit(); - ASSERT_TRUE(std::ranges::any_of(currentUnits, [generatedCurrentUnit](const Unit& currentUnit) + ASSERT_TRUE(std::ranges::any_of(scienceDefinition.currentUnits, [generatedCurrentUnit](const Unit& currentUnit) { return unitsAreEqual(generatedCurrentUnit, currentUnit); })); } + +INSTANTIATE_TEST_SUITE_P(TestScienceByLocale, ScienceTest, ValuesIn(locales), + [](const TestParamInfo& paramInfo) { return toString(paramInfo.param); }); From d5f5f316e390c3eb7ee72877576f8a865ab065fe Mon Sep 17 00:00:00 2001 From: Sumeet Khedkar Date: Tue, 3 Dec 2024 06:59:08 -0800 Subject: [PATCH 7/7] feat: Locale for internet module (#1007) * Added module constructor to both header (defination and the declaration * Reverting the changes as they are directly pushed into main * Added locale for science module(en_US) * fixed the formatting using clang-format * Added comments to the new parameters * Fixed the issue with formatting. * Added Locale to internet module Fixed the test cases to take locale as parameter Updated the header files to reflect changes made to the cpp files --- include/faker-cxx/internet.h | 19 ++-- src/modules/internet.cpp | 62 ++++++++---- src/modules/internet_data.h | 37 +++++-- tests/modules/internet_test.cpp | 174 +++++++++++++++++++++++--------- 4 files changed, 209 insertions(+), 83 deletions(-) diff --git a/include/faker-cxx/internet.h b/include/faker-cxx/internet.h index f976c42fc..5f3fe9cf0 100644 --- a/include/faker-cxx/internet.h +++ b/include/faker-cxx/internet.h @@ -81,7 +81,7 @@ FAKER_CXX_EXPORT std::string username(std::optional firstName = std */ FAKER_CXX_EXPORT std::string email(std::optional firstName = std::nullopt, std::optional lastName = std::nullopt, - std::optional emailHost = std::nullopt); + std::optional emailHost = std::nullopt, Locale locale = Locale::en_US); /** * @brief Generates an email address using the given person's name as base with example domain. @@ -96,7 +96,8 @@ FAKER_CXX_EXPORT std::string email(std::optional firstName = std::n * @endcode */ FAKER_CXX_EXPORT std::string exampleEmail(std::optional firstName = std::nullopt, - std::optional lastName = std::nullopt); + std::optional lastName = std::nullopt, + Locale locale = Locale::en_US); /** * @brief Generates a random password-like string. Do not use this method for generating actual passwords for users. @@ -201,7 +202,7 @@ FAKER_CXX_EXPORT unsigned httpStatusCode(std::optional respons * faker::internet::httpRequestHeader() // "Authorization" * @endcode */ -FAKER_CXX_EXPORT std::string_view httpRequestHeader(); +FAKER_CXX_EXPORT std::string_view httpRequestHeader(Locale locale = Locale::en_US); /** * @brief Generates a random http response header. @@ -212,7 +213,7 @@ FAKER_CXX_EXPORT std::string_view httpRequestHeader(); * faker::internet::httpResponseHeader() // "Location" * @endcode */ -FAKER_CXX_EXPORT std::string_view httpResponseHeader(); +FAKER_CXX_EXPORT std::string_view httpResponseHeader(Locale locale = Locale::en_US); /** * @brief Generates a random http media type. @@ -223,7 +224,7 @@ FAKER_CXX_EXPORT std::string_view httpResponseHeader(); * faker::internet::httpMediaType() // "application/json" * @endcode */ -FAKER_CXX_EXPORT std::string_view httpMediaType(); +FAKER_CXX_EXPORT std::string_view httpMediaType(Locale locale = Locale::en_US); /** * @brief Returns a string containing randomized ipv4 address of the given class. @@ -305,7 +306,7 @@ FAKER_CXX_EXPORT unsigned port(); * faker::internet::url() // "https://slow-timer.info" * @endcode */ -FAKER_CXX_EXPORT std::string url(const WebProtocol& webProtocol = WebProtocol::Https); +FAKER_CXX_EXPORT std::string url(const WebProtocol& webProtocol = WebProtocol::Https, Locale locale = Locale::en_US); /** * @brief Generates a random domain name. @@ -316,7 +317,7 @@ FAKER_CXX_EXPORT std::string url(const WebProtocol& webProtocol = WebProtocol::H * faker::internet::domainName() // "slow-timer.info" * @endcode */ -FAKER_CXX_EXPORT std::string domainName(); +FAKER_CXX_EXPORT std::string domainName(Locale locale = Locale::en_US); /** * @brief Generates a random domain word. @@ -338,7 +339,7 @@ FAKER_CXX_EXPORT std::string domainWord(); * faker::internet::domainSuffix() // "com" * @endcode */ -FAKER_CXX_EXPORT std::string_view domainSuffix(); +FAKER_CXX_EXPORT std::string_view domainSuffix(Locale locale = Locale::en_US); /** * @brief Generates a random username. @@ -388,5 +389,5 @@ getJWTToken(const std::optional>& header = st * faker::internet::getJWTAlgorithm(); // "HS256" * @endcode */ -FAKER_CXX_EXPORT std::string_view getJWTAlgorithm(); +FAKER_CXX_EXPORT std::string_view getJWTAlgorithm(Locale locale = Locale::en_US); } diff --git a/src/modules/internet.cpp b/src/modules/internet.cpp index 384143bc2..07ef976ec 100644 --- a/src/modules/internet.cpp +++ b/src/modules/internet.cpp @@ -111,6 +111,18 @@ std::string toJSON(std::map& data) namespace faker::internet { +namespace +{ +const struct InternetDefinition& getInternetDefinition(Locale locale) +{ + switch (locale) + { + default: + return enUSInternetDefinition; + } +} +} + namespace { const std::array webProtocols{"http", "https"}; @@ -194,16 +206,18 @@ std::string username(std::optional firstName, std::optional firstName, std::optional lastName, - std::optional emailHost) + std::optional emailHost, Locale locale) { + const auto& internetDefinition = getInternetDefinition(locale); return common::format("{}@{}", username(std::move(firstName), std::move(lastName)), - emailHost ? *emailHost : helper::randomElement(emailHosts)); + emailHost ? *emailHost : helper::randomElement(internetDefinition.emailHosts)); } -std::string exampleEmail(std::optional firstName, std::optional lastName) +std::string exampleEmail(std::optional firstName, std::optional lastName, Locale locale) { + const auto& internetDefinition = getInternetDefinition(locale); return common::format("{}@{}", username(std::move(firstName), std::move(lastName)), - helper::randomElement(emailExampleHosts)); + helper::randomElement(internetDefinition.emailExampleHosts)); } std::string password(int length, const PasswordOptions& options) @@ -294,19 +308,25 @@ unsigned httpStatusCode(std::optional responseType) return helper::randomElement(statusCodes); } -std::string_view httpRequestHeader() +std::string_view httpRequestHeader(Locale locale) { - return helper::randomElement(httpRequestHeaders); + const auto& internetDefinition = getInternetDefinition(locale); + + return helper::randomElement(internetDefinition.httpRequestHeaders); } -std::string_view httpResponseHeader() +std::string_view httpResponseHeader(Locale locale) { - return helper::randomElement(httpResponseHeaders); + const auto& internetDefinition = getInternetDefinition(locale); + + return helper::randomElement(internetDefinition.httpResponseHeaders); } -std::string_view httpMediaType() +std::string_view httpMediaType(Locale locale) { - return helper::randomElement(httpMediaTypes); + const auto& internetDefinition = getInternetDefinition(locale); + + return helper::randomElement(internetDefinition.httpMediaTypes); } std::string ipv4(const IPv4Class& ipv4class) @@ -396,16 +416,18 @@ unsigned port() return number::integer(65535u); } -std::string url(const WebProtocol& webProtocol) +std::string url(const WebProtocol& webProtocol, Locale locale) { + const auto& internetDefinition = getInternetDefinition(locale); + const auto protocolStr = webProtocol == WebProtocol::Https ? "https" : "http"; - return common::format("{}://{}", protocolStr, domainName()); + return common::format("{}://{}", protocolStr, domainName(locale)); } -std::string domainName() +std::string domainName(Locale locale) { - return common::format("{}.{}", domainWord(), domainSuffix()); + return common::format("{}.{}", domainWord(), domainSuffix(locale)); } std::string domainWord() @@ -413,9 +435,11 @@ std::string domainWord() return common::toLower(common::format("{}-{}", word::adjective(), word::noun())); } -std::string_view domainSuffix() +std::string_view domainSuffix(Locale locale) { - return helper::randomElement(domainSuffixes); + const auto& internetDefinition = getInternetDefinition(locale); + + return helper::randomElement(internetDefinition.domainSuffixes); } std::string anonymousUsername(unsigned maxLength) @@ -435,9 +459,11 @@ std::string anonymousUsername(unsigned maxLength) return common::format("{}{}", word::adjective(adjectiveLength), word::noun(nounLength)); } -std::string_view getJWTAlgorithm() +std::string_view getJWTAlgorithm(Locale locale) { - return helper::randomElement(jwtAlgorithms); + const auto& internetDefinition = getInternetDefinition(locale); + + return helper::randomElement(internetDefinition.jwtAlgorithms); } std::string getJWTToken(const std::optional>& header, diff --git a/src/modules/internet_data.h b/src/modules/internet_data.h index 1411a815c..7400ddacb 100644 --- a/src/modules/internet_data.h +++ b/src/modules/internet_data.h @@ -1,11 +1,24 @@ #pragma once #include +#include #include namespace faker::internet { -const auto domainSuffixes = std::to_array({ + +struct InternetDefinition +{ + std::span domainSuffixes; + std::span emailHosts; + std::span emailExampleHosts; + std::span httpMediaTypes; + std::span httpRequestHeaders; + std::span httpResponseHeaders; + std::span jwtAlgorithms; +}; + +const auto enUSdomainSuffixes = std::to_array({ "biz", "com", "info", @@ -14,14 +27,14 @@ const auto domainSuffixes = std::to_array({ "org", }); -const auto emailHosts = std::to_array({ +const auto enUSemailHosts = std::to_array({ "gmail.com", "hotmail.com", "outlook.com", "yahoo.com", }); -const auto emailExampleHosts = std::to_array({ +const auto enUSemailExampleHosts = std::to_array({ "example.com", "example.net", "example.org", @@ -739,7 +752,7 @@ const auto flagEmojis = std::to_array({ "🚩", }); -const auto httpMediaTypes = std::to_array({ +const auto enUShttpMediaTypes = std::to_array({ "application/gzip", "application/java-archive", "application/json", @@ -787,7 +800,7 @@ const auto httpMediaTypes = std::to_array({ "video/x-msvideo", }); -const auto httpRequestHeaders = std::to_array({ +const auto enUShttpRequestHeaders = std::to_array({ "A-IM", "Accept", "Accept-Charset", @@ -831,7 +844,7 @@ const auto httpRequestHeaders = std::to_array({ "Warning", }); -const auto httpResponseHeaders = std::to_array({ +const auto enUShttpResponseHeaders = std::to_array({ "Accept-CH", "Accept-Patch", "Accept-Ranges", @@ -878,7 +891,7 @@ const auto httpResponseHeaders = std::to_array({ "Warning", }); -const auto jwtAlgorithms = std::to_array({ +const auto enUSjwtAlgorithms = std::to_array({ "HS256", "HS384", "HS512", @@ -894,4 +907,14 @@ const auto jwtAlgorithms = std::to_array({ "none", }); +const InternetDefinition enUSInternetDefinition = { + .domainSuffixes = enUSdomainSuffixes, + .emailHosts = enUSemailHosts, + .emailExampleHosts = enUSemailExampleHosts, + .httpMediaTypes = enUShttpMediaTypes, + .httpRequestHeaders = enUShttpRequestHeaders, + .httpResponseHeaders = enUShttpResponseHeaders, + .jwtAlgorithms = enUSjwtAlgorithms, +}; + } diff --git a/tests/modules/internet_test.cpp b/tests/modules/internet_test.cpp index 6e7124d06..dec127f7f 100644 --- a/tests/modules/internet_test.cpp +++ b/tests/modules/internet_test.cpp @@ -55,7 +55,19 @@ std::array deconstructIpv4String(const std::string& ipv4) } } -class InternetTest : public Test +namespace +{ +const struct InternetDefinition& getInternetDefinition(Locale locale) +{ + switch (locale) + { + default: + return enUSInternetDefinition; + } +} +} + +class InternetTest : public TestWithParam { public: InternetTest() @@ -171,13 +183,17 @@ TEST_F(InternetTest, shouldGenerateInternationalUsernames) { return generatedUsername.find(common::toLower(lastName)) != std::string::npos; })); } -TEST_F(InternetTest, shouldGenerateEmail) +TEST_P(InternetTest, shouldGenerateEmail) { + const auto locale = GetParam(); + + const auto& internetDefinition = getInternetDefinition(locale); + std::vector firstNames(person::englishMaleFirstNames.begin(), person::englishMaleFirstNames.end()); firstNames.insert(firstNames.end(), person::englishFemaleFirstNames.begin(), person::englishFemaleFirstNames.end()); - const auto generatedEmail = email(); + const auto generatedEmail = email(std::nullopt, std::nullopt, std::nullopt, locale); const auto emailParts = common::split(generatedEmail, "@"); @@ -186,7 +202,7 @@ TEST_F(InternetTest, shouldGenerateEmail) const auto& generatedUsername = emailParts[0]; const auto& generatedEmailHost = emailParts[1]; - ASSERT_TRUE(std::ranges::any_of(emailHosts, [generatedEmailHost](const std::string_view& emailHost) + ASSERT_TRUE(std::ranges::any_of(internetDefinition.emailHosts, [generatedEmailHost](const std::string_view& emailHost) { return generatedEmailHost == emailHost; })); ASSERT_TRUE( std::ranges::any_of(firstNames, [generatedUsername](const std::string_view& firstName) @@ -196,11 +212,15 @@ TEST_F(InternetTest, shouldGenerateEmail) { return generatedUsername.find(common::toLower(lastName)) != std::string::npos; })); } -TEST_F(InternetTest, shouldGenerateEmailWithFirstName) +TEST_P(InternetTest, shouldGenerateEmailWithFirstName) { + const auto locale = GetParam(); + + const auto& internetDefinition = getInternetDefinition(locale); + const std::string firstName = "Tom"; - const auto generatedEmail = email(firstName); + const auto generatedEmail = email(firstName, std::nullopt, std::nullopt, locale); const auto emailParts = common::split(generatedEmail, "@"); @@ -209,7 +229,7 @@ TEST_F(InternetTest, shouldGenerateEmailWithFirstName) const auto& generatedUsername = emailParts[0]; const auto& generatedEmailHost = emailParts[1]; - ASSERT_TRUE(std::ranges::any_of(emailHosts, [generatedEmailHost](const std::string_view& emailHost) + ASSERT_TRUE(std::ranges::any_of(internetDefinition.emailHosts, [generatedEmailHost](const std::string_view& emailHost) { return generatedEmailHost == emailHost; })); ASSERT_TRUE(generatedUsername.find(common::toLower(firstName)) != std::string::npos); ASSERT_TRUE( @@ -217,15 +237,19 @@ TEST_F(InternetTest, shouldGenerateEmailWithFirstName) { return generatedUsername.find(common::toLower(lastName)) != std::string::npos; })); } -TEST_F(InternetTest, shouldGenerateEmailWithLastName) +TEST_P(InternetTest, shouldGenerateEmailWithLastName) { std::vector firstNames(person::englishMaleFirstNames.begin(), person::englishMaleFirstNames.end()); firstNames.insert(firstNames.end(), person::englishFemaleFirstNames.begin(), person::englishFemaleFirstNames.end()); + const auto locale = GetParam(); + + const auto& internetDefinition = getInternetDefinition(locale); + const std::string lastName = "Howard"; - const auto generatedEmail = email(std::nullopt, lastName); + const auto generatedEmail = email(std::nullopt, lastName, std::nullopt, locale); const auto emailParts = common::split(generatedEmail, "@"); @@ -234,7 +258,7 @@ TEST_F(InternetTest, shouldGenerateEmailWithLastName) const auto& generatedUsername = emailParts[0]; const auto& generatedEmailHost = emailParts[1]; - ASSERT_TRUE(std::ranges::any_of(emailHosts, [generatedEmailHost](const std::string_view& emailHost) + ASSERT_TRUE(std::ranges::any_of(internetDefinition.emailHosts, [generatedEmailHost](const std::string_view& emailHost) { return generatedEmailHost == emailHost; })); ASSERT_TRUE( std::ranges::any_of(firstNames, [generatedUsername](const std::string_view& firstName) @@ -242,13 +266,17 @@ TEST_F(InternetTest, shouldGenerateEmailWithLastName) ASSERT_TRUE(generatedUsername.find(common::toLower(lastName)) != std::string::npos); } -TEST_F(InternetTest, shouldGenerateEmailWithFullName) +TEST_P(InternetTest, shouldGenerateEmailWithFullName) { const std::string firstName = "Cindy"; const std::string lastName = "Young"; - const auto generatedEmail = email(firstName, lastName); + const auto locale = GetParam(); + + const auto& internetDefinition = getInternetDefinition(locale); + + const auto generatedEmail = email(firstName, lastName, std::nullopt, locale); const auto emailParts = common::split(generatedEmail, "@"); @@ -257,7 +285,7 @@ TEST_F(InternetTest, shouldGenerateEmailWithFullName) const auto& generatedUsername = emailParts[0]; const auto& generatedEmailHost = emailParts[1]; - ASSERT_TRUE(std::ranges::any_of(emailHosts, [generatedEmailHost](const std::string_view& emailHost) + ASSERT_TRUE(std::ranges::any_of(internetDefinition.emailHosts, [generatedEmailHost](const std::string_view& emailHost) { return generatedEmailHost == emailHost; })); ASSERT_TRUE(generatedUsername.find(common::toLower(firstName)) != std::string::npos); ASSERT_TRUE(generatedUsername.find(common::toLower(lastName)) != std::string::npos); @@ -289,13 +317,17 @@ TEST_F(InternetTest, shouldGenerateEmailWithSpecifiedEmailHost) { return generatedUsername.find(common::toLower(lastName)) != std::string::npos; })); } -TEST_F(InternetTest, shouldGenerateExampleEmail) +TEST_P(InternetTest, shouldGenerateExampleEmail) { + const auto locale = GetParam(); + + const auto& internetDefinition = getInternetDefinition(locale); + std::vector firstNames(person::englishMaleFirstNames.begin(), person::englishMaleFirstNames.end()); firstNames.insert(firstNames.end(), person::englishFemaleFirstNames.begin(), person::englishFemaleFirstNames.end()); - const auto email = exampleEmail(); + const auto email = exampleEmail(std::nullopt, std::nullopt, locale); const auto emailParts = common::split(email, "@"); @@ -304,7 +336,7 @@ TEST_F(InternetTest, shouldGenerateExampleEmail) const auto& generatedUsername = emailParts[0]; const auto& generatedEmailHost = emailParts[1]; - ASSERT_TRUE(std::ranges::any_of(emailExampleHosts, [generatedEmailHost](const std::string_view& emailHost) + ASSERT_TRUE(std::ranges::any_of(internetDefinition.emailExampleHosts, [generatedEmailHost](const std::string_view& emailHost) { return generatedEmailHost == emailHost; })); ASSERT_TRUE( std::ranges::any_of(firstNames, [generatedUsername](const std::string_view& firstName) @@ -314,11 +346,16 @@ TEST_F(InternetTest, shouldGenerateExampleEmail) { return generatedUsername.find(common::toLower(lastName)) != std::string::npos; })); } -TEST_F(InternetTest, shouldGenerateExampleEmailWithFirstName) +TEST_P(InternetTest, shouldGenerateExampleEmailWithFirstName) { + + const auto locale = GetParam(); + + const auto& internetDefinition = getInternetDefinition(locale); + const std::string firstName = "Barry"; - const auto email = exampleEmail(firstName); + const auto email = exampleEmail(firstName, std::nullopt, locale); const auto emailParts = common::split(email, "@"); @@ -327,7 +364,7 @@ TEST_F(InternetTest, shouldGenerateExampleEmailWithFirstName) const auto& generatedUsername = emailParts[0]; const auto& generatedEmailHost = emailParts[1]; - ASSERT_TRUE(std::ranges::any_of(emailExampleHosts, [generatedEmailHost](const std::string_view& emailHost) + ASSERT_TRUE(std::ranges::any_of(internetDefinition.emailExampleHosts, [generatedEmailHost](const std::string_view& emailHost) { return generatedEmailHost == emailHost; })); ASSERT_TRUE(generatedUsername.find(common::toLower(firstName)) != std::string::npos); ASSERT_TRUE( @@ -335,15 +372,19 @@ TEST_F(InternetTest, shouldGenerateExampleEmailWithFirstName) { return generatedUsername.find(common::toLower(lastName)) != std::string::npos; })); } -TEST_F(InternetTest, shouldGenerateExampleEmailWithLastName) +TEST_P(InternetTest, shouldGenerateExampleEmailWithLastName) { + const auto locale = GetParam(); + + const auto& internetDefinition = getInternetDefinition(locale); + std::vector firstNames(person::englishMaleFirstNames.begin(), person::englishMaleFirstNames.end()); firstNames.insert(firstNames.end(), person::englishFemaleFirstNames.begin(), person::englishFemaleFirstNames.end()); const std::string lastName = "Wilkinson"; - const auto email = exampleEmail(std::nullopt, lastName); + const auto email = exampleEmail(std::nullopt, lastName, locale); const auto emailParts = common::split(email, "@"); @@ -352,7 +393,7 @@ TEST_F(InternetTest, shouldGenerateExampleEmailWithLastName) const auto& generatedUsername = emailParts[0]; const auto& generatedEmailHost = emailParts[1]; - ASSERT_TRUE(std::ranges::any_of(emailExampleHosts, [generatedEmailHost](const std::string_view& emailHost) + ASSERT_TRUE(std::ranges::any_of(internetDefinition.emailExampleHosts, [generatedEmailHost](const std::string_view& emailHost) { return generatedEmailHost == emailHost; })); ASSERT_TRUE( std::ranges::any_of(firstNames, [generatedUsername](const std::string_view& firstName) @@ -360,13 +401,17 @@ TEST_F(InternetTest, shouldGenerateExampleEmailWithLastName) ASSERT_TRUE(generatedUsername.find(common::toLower(lastName)) != std::string::npos); } -TEST_F(InternetTest, shouldGenerateExampleEmailWithFullName) +TEST_P(InternetTest, shouldGenerateExampleEmailWithFullName) { + const auto locale = GetParam(); + + const auto& internetDefinition = getInternetDefinition(locale); + const std::string firstName = "Walter"; const std::string lastName = "Brown"; - const auto email = exampleEmail(firstName, lastName); + const auto email = exampleEmail(firstName, lastName, locale); const auto emailParts = common::split(email, "@"); @@ -375,7 +420,7 @@ TEST_F(InternetTest, shouldGenerateExampleEmailWithFullName) const auto& generatedUsername = emailParts[0]; const auto& generatedEmailHost = emailParts[1]; - ASSERT_TRUE(std::ranges::any_of(emailExampleHosts, [generatedEmailHost](const std::string_view& emailHost) + ASSERT_TRUE(std::ranges::any_of(internetDefinition.emailExampleHosts, [generatedEmailHost](const std::string_view& emailHost) { return generatedEmailHost == emailHost; })); ASSERT_TRUE(generatedUsername.find(common::toLower(firstName)) != std::string::npos); ASSERT_TRUE(generatedUsername.find(common::toLower(lastName)) != std::string::npos); @@ -543,28 +588,37 @@ TEST_F(InternetTest, shouldGenerateHttpStatusCode) { return generatedHttpStatusCode == statusCode; })); } -TEST_F(InternetTest, shouldGenerateHttpRequestHeader) +TEST_P(InternetTest, shouldGenerateHttpRequestHeader) { - const auto generatedHttpRequestHeader = httpRequestHeader(); + const auto locale = GetParam(); + + const auto& internetDefinition = getInternetDefinition(locale); - ASSERT_TRUE(std::ranges::any_of(httpRequestHeaders, [generatedHttpRequestHeader](const std::string_view& httpHeader) + const auto generatedHttpRequestHeader = httpRequestHeader(locale); + + ASSERT_TRUE(std::ranges::any_of(internetDefinition.httpRequestHeaders, [generatedHttpRequestHeader](const std::string_view& httpHeader) { return generatedHttpRequestHeader == httpHeader; })); } -TEST_F(InternetTest, shouldGenerateHttpResponseHeader) +TEST_P(InternetTest, shouldGenerateHttpResponseHeader) { - const auto generatedHttpResponseHeader = httpResponseHeader(); + const auto locale = GetParam(); + const auto& internetDefinition = getInternetDefinition(locale); + const auto generatedHttpResponseHeader = httpResponseHeader(locale); - ASSERT_TRUE(std::ranges::any_of(httpResponseHeaders, + ASSERT_TRUE(std::ranges::any_of(internetDefinition.httpResponseHeaders, [generatedHttpResponseHeader](const std::string_view& httpHeader) { return generatedHttpResponseHeader == httpHeader; })); } -TEST_F(InternetTest, shouldGenerateHttpMediaType) +TEST_P(InternetTest, shouldGenerateHttpMediaType) { - const auto generatedHttpMediaType = httpMediaType(); + const auto locale = GetParam(); + const auto& internetDefinition = getInternetDefinition(locale); + + const auto generatedHttpMediaType = httpMediaType(locale); - ASSERT_TRUE(std::ranges::any_of(httpMediaTypes, [generatedHttpMediaType](const std::string_view& httpMediaType) + ASSERT_TRUE(std::ranges::any_of(internetDefinition.httpMediaTypes, [generatedHttpMediaType](const std::string_view& httpMediaType) { return generatedHttpMediaType == httpMediaType; })); } @@ -702,11 +756,15 @@ TEST_F(InternetTest, MacDefaultSeparatorOverwrite) } } -TEST_F(InternetTest, shouldGenerateDomainSuffix) +TEST_P(InternetTest, shouldGenerateDomainSuffix) { - const auto generatedDomainSuffix = domainSuffix(); + const auto locale = GetParam(); + + const auto& internetDefinition = getInternetDefinition(locale); - ASSERT_TRUE(std::ranges::any_of(domainSuffixes, [generatedDomainSuffix](const std::string_view& domainSuffix) + const auto generatedDomainSuffix = domainSuffix(locale); + + ASSERT_TRUE(std::ranges::any_of(internetDefinition.domainSuffixes, [generatedDomainSuffix](const std::string_view& domainSuffix) { return generatedDomainSuffix == domainSuffix; })); } @@ -717,9 +775,12 @@ TEST_F(InternetTest, shouldGenerateDomainWord) assertDomainWord(generatedDomainWord); } -TEST_F(InternetTest, shouldGenerateDomainName) +TEST_P(InternetTest, shouldGenerateDomainName) { - const auto generatedDomainName = domainName(); + const auto locale = GetParam(); + + const auto& internetDefinition = getInternetDefinition(locale); + const auto generatedDomainName = domainName(locale); const auto generatedDomainNameParts = common::split(generatedDomainName, "."); @@ -727,13 +788,18 @@ TEST_F(InternetTest, shouldGenerateDomainName) const auto& generatedDomainSuffix = generatedDomainNameParts[1]; assertDomainWord(generatedDomainWord); - ASSERT_TRUE(std::ranges::any_of(domainSuffixes, [generatedDomainSuffix](const std::string_view& domainSuffix) + ASSERT_TRUE(std::ranges::any_of(internetDefinition.domainSuffixes, [generatedDomainSuffix](const std::string_view& domainSuffix) { return generatedDomainSuffix == domainSuffix; })); } -TEST_F(InternetTest, shouldGenerateHttpsUrl) +TEST_P(InternetTest, shouldGenerateHttpsUrl) { - const auto generatedUrl = url(); + + const auto locale = GetParam(); + + const auto& internetDefinition = getInternetDefinition(locale); + + const auto generatedUrl = url(WebProtocol::Https, locale); const auto generatedUrlParts = common::split(generatedUrl, "://"); @@ -746,14 +812,18 @@ TEST_F(InternetTest, shouldGenerateHttpsUrl) const auto& generatedDomainSuffix = generatedDomainNameParts[1]; assertDomainWord(generatedDomainWord); - ASSERT_TRUE(std::ranges::any_of(domainSuffixes, [generatedDomainSuffix](const std::string_view& domainSuffix) + ASSERT_TRUE(std::ranges::any_of(internetDefinition.domainSuffixes, [generatedDomainSuffix](const std::string_view& domainSuffix) { return generatedDomainSuffix == domainSuffix; })); ASSERT_EQ(generatedProtocol, "https"); } -TEST_F(InternetTest, shouldGenerateHttpUrl) +TEST_P(InternetTest, shouldGenerateHttpUrl) { - const auto generatedUrl = url(WebProtocol::Http); + const auto locale = GetParam(); + + const auto& internetDefinition = getInternetDefinition(locale); + + const auto generatedUrl = url(WebProtocol::Http, locale); const auto generatedUrlParts = common::split(generatedUrl, "://"); @@ -766,7 +836,7 @@ TEST_F(InternetTest, shouldGenerateHttpUrl) const auto& generatedDomainSuffix = generatedDomainNameParts[1]; assertDomainWord(generatedDomainWord); - ASSERT_TRUE(std::ranges::any_of(domainSuffixes, [generatedDomainSuffix](const std::string_view& domainSuffix) + ASSERT_TRUE(std::ranges::any_of(internetDefinition.domainSuffixes, [generatedDomainSuffix](const std::string_view& domainSuffix) { return generatedDomainSuffix == domainSuffix; })); ASSERT_EQ(generatedProtocol, "http"); } @@ -819,10 +889,16 @@ TEST_F(InternetTest, shouldGenerateJwtToken) ASSERT_TRUE(std::regex_match(getJWTToken(header, payload, refDate), pattern)); } -TEST_F(InternetTest, shouldGenerateJWTAlgorithm) +TEST_P(InternetTest, shouldGenerateJWTAlgorithm) { - const auto generatedJWTAlgorythm = getJWTAlgorithm(); + const auto locale = GetParam(); + const auto& internetDefinition = getInternetDefinition(locale); - ASSERT_TRUE(std::ranges::any_of(jwtAlgorithms, [generatedJWTAlgorythm](const std::string_view& JWTAlgorythm) + const auto generatedJWTAlgorythm = getJWTAlgorithm(locale); + + ASSERT_TRUE(std::ranges::any_of(internetDefinition.jwtAlgorithms, [generatedJWTAlgorythm](const std::string_view& JWTAlgorythm) { return generatedJWTAlgorythm == JWTAlgorythm; })); } + +INSTANTIATE_TEST_SUITE_P(TestInternetByLocale, InternetTest, ValuesIn(locales), + [](const TestParamInfo& paramInfo) { return toString(paramInfo.param); }); \ No newline at end of file