From f0e81b35ecdf05f2727a31ce0a0771ed30eaa25a Mon Sep 17 00:00:00 2001
From: iamdual <imduual@gmail.com>
Date: Sat, 18 May 2024 00:43:19 +0300
Subject: [PATCH] make the raw values of client hints public

---
 src/ua_generator/client_hints.py | 47 ++++++++++++++++++--------------
 tests/test_client_hints.py       | 34 +++++++++++++++++++++++
 2 files changed, 60 insertions(+), 21 deletions(-)

diff --git a/src/ua_generator/client_hints.py b/src/ua_generator/client_hints.py
index 1be2322..8e2cc0f 100644
--- a/src/ua_generator/client_hints.py
+++ b/src/ua_generator/client_hints.py
@@ -3,7 +3,9 @@
 Copyright: 2022-2024 Ekin Karadeniz (github.com/iamdual)
 License: Apache License 2.0
 """
-from . import formats, serialization, utils
+from random import Random
+
+from . import formats, serialization
 from .data import platforms_mobile
 from .data import generator
 
@@ -26,10 +28,10 @@ def __init__(self, gen: generator.Generator):
         self.__generator = gen
         self.__cache = {}
 
-    def __mobile(self):
+    def get_mobile(self):
         return self.__generator.platform in platforms_mobile
 
-    def __platform(self):
+    def get_platform(self):
         platform = self.__generator.platform
 
         if platform == 'ios':
@@ -41,13 +43,14 @@ def __platform(self):
 
         return platform
 
-    def __platform_version(self):
+    def get_platform_version(self):
         if self.__generator.platform == 'windows' and formats.major_version(self.__generator.platform_version) == '10':
-            return utils.choice(('10.0.0', '13.0.0'))
+            _random = Random(self.__generator.user_agent)
+            return _random.choice(('10.0.0', '13.0.0'))
 
         return formats.version(self.__generator.platform_version)
 
-    def __brands(self, full_version_list: bool = False):
+    def get_brands(self, full_version_list: bool = False):
         brand_list = [{'brand': 'Not A(Brand', 'version': '99'}]
 
         if full_version_list:
@@ -64,27 +67,29 @@ def __brands(self, full_version_list: bool = False):
 
         return brand_list
 
-    def __bitness(self):
+    def get_bitness(self):
         if self.__generator.platform == 'android':
-            return utils.choice(('32', '64', '32', '32'))
+            _random = Random(self.__generator.user_agent)
+            return _random.choice(('32', '64', '32', '32'))
 
         return '64'
 
-    def __architecture(self):
+    def get_architecture(self):
         if self.__generator.platform == 'android' or self.__generator.platform == 'ios':
             return 'arm'
         elif self.__generator.platform == 'macos':
-            return utils.choice(('arm', 'x86', 'arm', 'arm'))
+            _random = Random(self.__generator.user_agent)
+            return _random.choice(('arm', 'x86', 'arm', 'arm'))
 
         return 'x86'
 
-    def __model(self):
+    def get_model(self):
         if 'platform_model' in self.__generator.platform_version:
             return self.__generator.platform_version['platform_model']
 
         return ''
 
-    def __wow64(self):
+    def get_wow64(self):
         return self.__generator.platform == 'windows'
 
     def __getattr__(self, name):
@@ -92,23 +97,23 @@ def __getattr__(self, name):
             return self.__cache[name]
 
         if name == 'mobile':
-            self.__cache[name] = serialization.ch_bool(self.__mobile())
+            self.__cache[name] = serialization.ch_bool(self.get_mobile())
         elif name == 'platform':
-            self.__cache[name] = serialization.ch_string(self.__platform())
+            self.__cache[name] = serialization.ch_string(self.get_platform())
         elif name == 'platform_version':
-            self.__cache[name] = serialization.ch_string(self.__platform_version())
+            self.__cache[name] = serialization.ch_string(self.get_platform_version())
         elif name == 'brands':
-            self.__cache[name] = serialization.ch_brand_list(self.__brands())
+            self.__cache[name] = serialization.ch_brand_list(self.get_brands())
         elif name == 'brands_full_version_list':
-            self.__cache[name] = serialization.ch_brand_list(self.__brands(full_version_list=True))
+            self.__cache[name] = serialization.ch_brand_list(self.get_brands(full_version_list=True))
         elif name == 'bitness':
-            self.__cache[name] = serialization.ch_string(self.__bitness())
+            self.__cache[name] = serialization.ch_string(self.get_bitness())
         elif name == 'architecture':
-            self.__cache[name] = serialization.ch_string(self.__architecture())
+            self.__cache[name] = serialization.ch_string(self.get_architecture())
         elif name == 'model':
-            self.__cache[name] = serialization.ch_string(self.__model())
+            self.__cache[name] = serialization.ch_string(self.get_model())
         elif name == 'wow64':
-            self.__cache[name] = serialization.ch_bool(self.__wow64())
+            self.__cache[name] = serialization.ch_bool(self.get_wow64())
 
         return self.__cache[name]
 
diff --git a/tests/test_client_hints.py b/tests/test_client_hints.py
index 79eefff..db21fc9 100644
--- a/tests/test_client_hints.py
+++ b/tests/test_client_hints.py
@@ -6,6 +6,7 @@
 import unittest
 
 import src.ua_generator as ua_generator
+from src.ua_generator import serialization
 from src.ua_generator.data import browsers_support_ch
 
 
@@ -14,13 +15,17 @@ def test_ch_platform(self):
         for i in range(0, 100):
             ua = ua_generator.generate(browser=browsers_support_ch, platform='macos')
             self.assertIsNotNone(ua.ch)
+            self.assertTrue(type(ua.ch.platform) is str)
             self.assertEqual(ua.ch.platform, '"macOS"')
+            self.assertEqual(ua.ch.get_platform(), 'macOS')
 
     def test_ch_platform_2(self):
         for i in range(0, 100):
             ua = ua_generator.generate(browser=browsers_support_ch, platform='linux')
             self.assertIsNotNone(ua.ch)
+            self.assertTrue(type(ua.ch.platform) is str)
             self.assertEqual(ua.ch.platform, '"Linux"')
+            self.assertEqual(ua.ch.get_platform(), 'Linux')
 
     def test_ch_platform_version(self):
         for i in range(0, 100):
@@ -28,6 +33,7 @@ def test_ch_platform_version(self):
             self.assertIsNotNone(ua.ch)
             self.assertTrue(type(ua.ch.platform_version) is str)
             self.assertTrue(len(ua.ch.platform_version) > 0)
+            self.assertEqual(ua.ch.platform_version, serialization.ch_string(ua.ch.get_platform_version()))
 
     def test_ch_platform_version_windows(self):
         for i in range(0, 100):
@@ -35,71 +41,99 @@ def test_ch_platform_version_windows(self):
             self.assertIsNotNone(ua.ch)
             self.assertTrue(type(ua.ch.platform_version) is str)
             self.assertEqual(len(ua.ch.platform_version.split('.')), 3)
+            self.assertEqual(ua.ch.platform_version, serialization.ch_string(ua.ch.get_platform_version()))
 
     def test_ch_mobile(self):
         for i in range(0, 100):
             ua = ua_generator.generate(browser=browsers_support_ch, platform='android')
             self.assertIsNotNone(ua.ch)
+            self.assertTrue(type(ua.ch.mobile) is str)
             self.assertEqual(ua.ch.mobile, '?1')
+            self.assertTrue(type(ua.ch.get_mobile()) is bool)
+            self.assertEqual(ua.ch.get_mobile(), True)
 
     def test_ch_non_mobile(self):
         for i in range(0, 100):
             ua = ua_generator.generate(browser=browsers_support_ch, platform='windows')
             self.assertIsNotNone(ua.ch)
+            self.assertTrue(type(ua.ch.mobile) is str)
             self.assertEqual(ua.ch.mobile, '?0')
+            self.assertTrue(type(ua.ch.get_mobile()) is bool)
+            self.assertEqual(ua.ch.get_mobile(), False)
 
     def test_ch_brands(self):
         for i in range(0, 100):
             ua = ua_generator.generate(browser='chrome', platform='windows')
             self.assertIsNotNone(ua.ch)
+            self.assertTrue(type(ua.ch.brands) is str)
             self.assertTrue(ua.ch.brands.startswith('"Not A(Brand";v="99"'))
             self.assertTrue('Chromium' in ua.ch.brands)
             self.assertTrue('Google Chrome' in ua.ch.brands)
+            self.assertTrue(type(ua.ch.get_brands()) is list)
+            self.assertEqual(ua.ch.brands, serialization.ch_brand_list(ua.ch.get_brands()))
 
     def test_ch_brands_full_version_list(self):
         for i in range(0, 100):
             ua = ua_generator.generate(browser='edge', platform='windows')
             self.assertIsNotNone(ua.ch)
+            self.assertTrue(type(ua.ch.brands_full_version_list) is str)
             self.assertTrue(ua.ch.brands_full_version_list.startswith('"Not A(Brand";v="99"'))
             self.assertTrue('Chromium' in ua.ch.brands_full_version_list)
             self.assertTrue('Microsoft Edge' in ua.ch.brands_full_version_list)
+            self.assertTrue(type(ua.ch.get_brands(full_version_list=True)) is list)
+            self.assertEqual(ua.ch.brands_full_version_list,
+                             serialization.ch_brand_list(ua.ch.get_brands(full_version_list=True)))
 
     def test_ch_bitness(self):
         for i in range(0, 100):
             ua = ua_generator.generate(browser=browsers_support_ch)
             self.assertIsNotNone(ua.ch)
+            self.assertTrue(type(ua.ch.bitness) is str)
             self.assertIn(ua.ch.bitness, ('"32"', '"64"'))
+            self.assertIn(ua.ch.get_bitness(), ('32', '64'))
 
     def test_ch_architecture(self):
         for i in range(0, 100):
             ua = ua_generator.generate(browser=browsers_support_ch)
             self.assertIsNotNone(ua.ch)
+            self.assertTrue(type(ua.ch.architecture) is str)
             self.assertIn(ua.ch.architecture, ('"arm"', '"x86"'))
+            self.assertIn(ua.ch.get_architecture(), ('arm', 'x86'))
 
     def test_ch_model(self):
         for i in range(0, 100):
             ua = ua_generator.generate(platform='android', browser='chrome')
             self.assertIsNotNone(ua.ch)
+            self.assertTrue(type(ua.ch.model) is str)
             self.assertTrue(ua.ch.model != '""')
             self.assertTrue(len(ua.ch.model) > 2)
+            self.assertEqual(ua.ch.model, serialization.ch_string(ua.ch.get_model()))
 
     def test_ch_model_2(self):
         for i in range(0, 100):
             ua = ua_generator.generate(platform='linux', browser='firefox')
             self.assertIsNotNone(ua.ch)
+            self.assertTrue(type(ua.ch.model) is str)
             self.assertTrue(ua.ch.model == '""')
+            self.assertEqual(ua.ch.model, serialization.ch_string(ua.ch.get_model()))
 
     def test_ch_wow64(self):
         for i in range(0, 100):
             ua = ua_generator.generate(platform='windows')
             self.assertIsNotNone(ua.ch)
+            self.assertTrue(type(ua.ch.wow64) is str)
             self.assertEqual(ua.ch.wow64, '?1')
+            self.assertTrue(type(ua.ch.get_wow64()) is bool)
+            self.assertEqual(ua.ch.get_wow64(), True)
 
     def test_ch_wow64_2(self):
         for i in range(0, 100):
             ua = ua_generator.generate(platform='linux')
             self.assertIsNotNone(ua.ch)
+            self.assertTrue(type(ua.ch.wow64) is str)
             self.assertEqual(ua.ch.wow64, '?0')
+            self.assertTrue(type(ua.ch.get_wow64()) is bool)
+            self.assertEqual(ua.ch.get_wow64(), False)
 
 
 if __name__ == '__main__':