Hello everyone and welcome to Retro Tech. I hope I can finally get this right. I've been trying to record this CRT in action for a little while today. Let me go through a little bit of what I'm doing in today's episode. So I've been getting some requests to see some of the CRTs actually in action. And I thought, well, that's a great idea. And why not start with this Sony, or I mean not Sony, excuse me, this JVCD series 36 inch big boy. That is a model from the year 2000. And it has just about every analog input you can put into it. It does not have RGB SCART, but it does have component. And that's what I'm using today. I'm using my SNES SNES Mini or Junior. And this one does have the 7374 amp installed in it today. Well, it wasn't installed today, but it started a long time ago. And that's what I'm using for my conversion. It's converting that RGB signal to component. And then that's what's being fed to the TV. And it gives a nice scan line effect and really sharp picture. And it's just as good as RGB would be on this television. So today I figured I'd go with a Capcom theme. And we're going to start with a couple of Capcom games. And the first one's going to be Demon Crest. And these were suggested to me in the last video. And I will show you. Let's just go ahead and get into some of these games.  All right, Demon's Crest. So as I'm adjusting the camera here, let's get set up to try to play some of this. I want you to hopefully see how good this looks on this television. Just running through again component. This is the RGB converted to component. I'll talk you through this a little bit. I don't see any reason if your TV already gets component to convert it to RGB. So let's get in here. We'll just go ahead and start. Play the first level or so. This is really a great game. It shows a lot of good colors and action here. Speckling Capcom used to be one of the top video game for developers and production companies. So obviously I'm playing this through a ROM through my SD to SNES. And some things to note about this is where there's been a lot of news about ROMs thanks to Nintendo lately. They've sent some serious letters and threats to some of the ROM sites in the community as well as, you know, sued them now.  Oh, whether it was through advertisers or whatever, they did make some money off of Nintendo's intellectual property which I can understand them having a problem with. But on the flip side of that, you've got to understand what we're trying to do. We want to play retro video games on the original systems and we don't want to go paying thousands or hundreds of dollars to pay you play one game that might be rare. Or even if you own the game, you don't want to damage your game copy by playing it or scratching it or, you know, putting it in and out of a machine. So that's where Nintendo comes off as a bad guy. See, I would have respected this plan if they would have had something set up where you could go ahead and buy the ROM packs. I mean, why don't they just sell us the ROM packs? Think about that. If Nintendo themselves sold ROM packs for hardware like this or for, you know, emulation programs, if they sold them themselves and offered them and they were free of viruses, it would be just like when Napster was around and the whole MP3 thing got settled. Because people eventually, you know, eventually if you want people to stop, just make them have viruses in them and people will stop downloading your stuff. So, but anyway, Nintendo looks like a bad guy here because they went and took the whole legal route. And the only one they're really hurting is their fans because, I mean, big deal if the ROM sites go out of business, I guess, right? Who cares? I mean, they weren't really doing anything, but that hurts us if there's no way for us to get good quality ROMs. So I'm really kind of frustrated with the way Nintendo's handled that whole thing. I hope maybe that they could take a second and think about how they could have done that the right way. And rather than turning something into bad press, what they could have done was turn it into a very good press and good service by coming out with something to sell these ROMs and offer them to the marketplace and say, look, if anybody else is selling our ROMs, you're going to stop. And everybody would understand. It would make complete sense. But when you just tell people to stop doing something because you're not smart enough to figure out a way to make money off of it, or you're just too lazy and don't care about the product and don't want to support people who can do a lot to play old systems that have been obsolete for a long time that are retro, I get it. I mean, I understand that, but it's a bad decision, okay? It's a bad business decision. And it happens. Big businesses make bad business decisions all the time. So anyway, while we're playing, it's no big deal to get off on a rant about Nintendo and what they've been doing wrong in the wrong community. So if you have any opinion on it, you don't leave it in here. Let's let them know what we feel. This is a very cool game. I mean, man, just like, there we go. So let's go see if we can beat this guy up. I've had some trouble trying to get this thing nailed down. I've been changing my shutter speeds on my camera and also trying to change the angle as well as the brightness, because it doesn't look the same as it does in the naked eye. But it looks great. You know, if you like to play these old games, nothing's better than this. If you just did, for example, what I've done now, let's not say, you know, you could get the TV for $30. It's about how much this one was, $30, $50 maybe, for a really high end, top of the line, large CRT. And again, this one has the right aspect ratio, and it also has the curved screen, so that's important. But, you know, you get one of these and you get the component cables for $50, okay? But I wouldn't even recommend that because the component cables are great. They're great performing products, but they're expensive. And it's only good for, like, one kind of system, pretty much, and you have to switch them back and forth. I think that the best idea is to go from SCART to component, RGB to component, with a converter. There's a nice converter on eBay that's about $80, shipped. I'll put a link to it in the description of this video, so you can take a look at that. But I really think that that would be the better idea simply because so many systems have good SCART cables ready for them, as is right now. And again, with the component cables, you've got to wait for the production. And then they aren't even like, you know, HD Retrovision, they made a great product. But I don't think that they're, you know, they were too excited about being known as just a cable company. I think they had more things in mind. And so that just made it so they didn't really, you know, they don't want to consider how to make component cables and service them all day, which I get another, I don't really think it's a good business decision. I think, again, that they should set something up where they could have some kind of streamlined or sell their product to someone else to let them sell it for us, but for them. Because I just feel that, like, if this actually grows, they've got a product now. So what they're going to risk is being put out of the marketplace by a competitor.  But anyway, that's just my two cents on that too. So again, I would say that, you know, first choice, go with SCART and then get it converted to component. And then that's just the way you should do it if you can't get your hands on RGB monitor right away. Because RGB monitors are tough, they're expensive, they are awesome. But I think that if you're just looking for a great experience, people need to, people need to also take a look at some of these higher end TVs, just regular CRT TVs that, you know, you don't have to always go with Sony. JVC made plenty of broadcast monitors. Panasonic was the top brand at this time, period. There are a couple other brands. I actually have a Toshiba that's a 32 inch that is a very nice CRT that I'll feature in an upcoming video too. But I think that's enough of Demon's Crest. So let's go ahead back out to our main menu and go with our second game of the night. I'm only going to do two games tonight. And I'm hoping that, I'm hoping they're coming through because I've tried to film this, I think, three or four times. And every time it comes out blurry or it just doesn't look as good. I'm sorry, I don't know that I'll ever be able to get to show the scan lines that great on just a, using just a camera and videotaping straight off the CRT. But we're going to keep trying. Maybe if you have any suggestions on how to make this work better, this is a Nikon camera. So anyway, if you have any suggestions, I will take them for sure. So let's get in here our Super Ghouls and Ghosts. This is another one that was, alright, there it is. That's a Capcom game that was recommended for this. And I love it. I grew up on this game. This was one of the games I used to just play nonstop. So I thought it was a good one. And I can already tell just kind of like when I look back and forth between the screens, it's a lot brighter on the camera than it is on the CRT. I've had to turn the brightness down quite a bit. Every time it gets bright from the screen, you can see it kind of bleed through in the camera. So it's a great game again. I'm not sure again how it looks. I'm going to go ahead and just publish this video. If you don't like it, please just let me know. I mean, don't have to worry about hurting my feelings. I'm completely fine with people telling me whether this doesn't look good or if it's a waste of time. And they think I should just try to stick to other things. But I kind of thought that maybe somebody would like to see this, see how it looks. And get an idea for what it looks like before making a commitment to either spending an extra large amount of money on a RGB monitor or just going for starting with something like this. Some other things to note that I still really like the reason I like these old TVs. So you can play anything on them, okay? And they have great sound systems built into them usually. Stereo. So that's one thing, stereo sound system. And then you can play anything on them so you can play anything. Anything that goes back to just having only RF support without modification. And some of those systems look good in RF. The original Atari systems actually looked quite good in RF. The fours and six switchers, the original ones. So some of those things. And then some people don't even think that things work because they can't even test RF switches. And I can tell you how many RF consoles I get for five bucks because people just don't even know how to test them. They just sell them as is. So that and also, you know, I use Roku's which is a streaming device. And I stream Netflix and other shows onto this because you can still get some of those classic shows that were meant for this 4.3 format. And it looks great on composite. Even just composite. It was made for that. So composite and 480i looks fantastic on this. I've done VCR, I've done streaming, I've even done converter boxes for cable. That's another good option. If you want to just watch some TV on it and you shop, you want to add a high def, you know, a high def receiving antenna. And then get a converter box and convert that signal to whatever the TV takes. Oh boy, that's it. Okay. So that's the brutal part of this game. It's time to get thrown back and start all over the beginning. Anyway, so I hope you can kind of see. I really do hope this came out because again, I'm just going to publish this no matter what. So if it doesn't look good, let me know. If you can't see scan lines and you like the effect, please let me know too. And if you do like it, give it a thumbs up and give me a game suggestion. I've got just about every gaming console that does analog and I'd be glad to throw it on here and try to find a game. At least, you know, give me a system and then give me five games and I can get two out of there. So I really love light gun games. I might try to do that on the next one. One of the systems with some light guns. I've got a big collection of light gun games. But that's going to do it for today. I appreciate you watching this special with me and Retro Tech. If you haven't already hit subscribe and hit the notification bell so you know when we got new content coming out. I will have a new video coming up later this week with the newest information on comics, what's hot, what's not, and what's a good read, what should you spec on. But if you're not, but thanks again for watching this with us and have a great evening.


Ladies and gentlemen, it is a pleasure to join you virtually for the third PIS Innovation Summit. I thank you for this opportunity to put financial inclusion at the heart of the discussion. We come together at a difficult and uncertain time, from COVID to conflict, to inflation and climate change. We're all seeking new ways to tackle the challenges before us. As we know, technological innovation offers enormous opportunities. As UN Secretary General's special advocate, I've witnessed the power of digital innovation to build resilience, endure shocks, and bolster financial inclusion. For example, the rapid growth in mobile phone news and new customer data trails offer exciting new ways to reach the underserved and change lives for the better. I recently saw this firsthand in Tanzania. There, I met with small-holder farmers mitigating extreme weather impacts for the first time with the help of a former financial service provider called Acre Africa. The company offers affordable micro-insurance to farmers using weather satellite data to assess and predict risks. When disaster strikes, this data is used to trigger digital payouts that farmers receive automatically. This innovative set of services helps the farmers become more resilient to climate change. And the confidence gained from insurance coverage has spurred them to seize new opportunities. They now invest in drought-resistant seeds and fertilizers to boost crop yields. When innovation can set off a change of benefits, but it can also bring new risks in both advanced economies and emerging markets. For instance, financial services that use artificial intelligence can help overcome many traditional barriers people often face to obtain a loan. But at the same time, it might use algorithms that create patterns of bias and misinterpret data. This can especially affect underserved groups like women, small business owners, and the elderly, among others. Another issue is appropriate and adequate data governance. This is urgent, as Big Tech platforms look to use customers' data footprints to embed financial services in social media, e-commerce, and entertainment. Meanwhile, customers often do not know the value of their own data. Most grant consent for data usage without reading the terms and conditions, which are usually long and hard to comprehend. They also rarely benefit from their own data. For example, it is nearly impossible for customers to access and share their data trails to receive lower insurance quotes or interest rates. Individuals and MSMEs could be empowered with their own data, and bias in algorithms should be addressed to become more inclusive. To achieve this, we can start by better understanding new risks. Then, accelerating collaboration to ensure technological innovation puts customers at the center. For example, by developing audit tools and putting ombudspersons in place to assess algorithms on behalf of customers or building balanced data sets that are reviewed and analyzed to reduce historical bias. A digital public infrastructure will be needed to enable these innovations in the public and private sectors. For instance, greater connectivity and digital ID can open access to financial services for millions left behind. Fair competition and interoperable payment systems can help markets work better for even the smaller skills customers. Cybersecurity, data governance, and digital literacy can help marginalized communities navigate digital innovation with more confidence. During the summit, you can discuss all these important issues, taking stock of the opportunities and of the risks. Let us ask, how can we better create and share approaches to mitigate bias from algorithms? What more can we do to empower individuals and MSMEs with their own data? How can we encourage the development and financial products that enhance customers' resilience? And how can the public and private sectors work together to create digital public infrastructure that enables transformative opportunities for the underserved? We are lucky to have many leading global policymakers and senior leaders in finance, technology, and academia here today. Your role is crucial. I wish you every success in your efforts to create a real, innovative digital financial system that benefits everyone.


Everybody, it's Brian, and in this episode we're gonna talk about the QMap class. This is a confusing class for many, many reasons, but basically the big confusion, it's a templated class, but it takes two different types. A key and a value. It forms what's called an associative array using a key value pair. Feel free to dive into the documentation. It does get a little murky the deeper you go. But at its core, it looks something like this. Instead of using a positional index like 0, 1, 2, you can use a key. And that can be any type. For example, we can use a string, let's call this key 57. Let's call this e22, notice these don't actually have to be in any specific order. Then this one could be called cat. It simply doesn't matter. As long as you follow the same type in this example of string, that key has to be unique. That key will point, or I should say associate, with a value that is also stored. That forms the associative array. These values don't have to be unique. And guess what? This can just add and add and add all you want. It's actually extremely flexible. Let's dive in and look at a really, really basic example here. So I'm gonna say QMap, and because this is templated, I have to give it what we're working with. I'm gonna say QString, comma, and we want a double for a value. So the key's always first, the value's always the last one. And I'm gonna call this lucky. So this is just simply our lucky numbers. And I'm gonna say lucky dot, let's go ahead and insert. And I need our key, which in this case, because our ID is awesome, it already knows the key is going to be a QString. And then we need a value, which is a double. So let's go ahead and give it a QString. And let's go ahead and give it a value, which in this case needs to be a double. So I'm gonna say 22.5. From here, we can do multiple things. I'm just gonna say QInfo, and let's just print out the entire map. And we can also say QInfo, my lucky number is. And let's go ahead and access that. So I'm gonna say lucky. And then we're going to get it by the key, not by the position. Remember, our key is this right here, the first one. Save, run, let's see what this looks like, just as a simple example. Sure enough, there's our QMap, we have our key value pair. And we can say, my lucky number is, and then use the key to get the value. Let's dive really deep into this class and see how it works. Okay, one of the major complexities about QMap is how do you work with QMap with things beyond simple data types, for example, doubles or QStrings or integers. We wanna work with a QObject. So I have this cat class all ready to go. This is a QObject, which of course has its own complexities, the biggest being we cannot copy this. We've talked about this in previous videos, QObjects cannot be copied. You can see, while this is not a super complex class, it's more complex than say an integer or a bool. It's got getters and setters and private variables and things of that nature. So, let's go ahead and look at how we would create a QMap. I don't wanna use simple data types because, well, it's too simple, and you won't really understand QMap and how it works. So instead, let's make a type def, this is short for type definition. And basically, we're going to shorten or make an alias of the type. The type we're gonna work with is a QMap, which is a template. And we need to give it a key value pair. So the key is going to be a QString. The value, though, is going to be an instance of this cat class. Now, because we cannot copy, we can't just, you guessed it, give it a normal instance, we have to give it a pointer. So this is where the complexity jumps in. You can either use just a raw pointer, and then you have to manage the memory, or you can use the standard library, smart pointers, or because this is a cute video, we're gonna use the Q shared pointer. This is also a templated class, and this is why this gets super confusing. So rather than retype this every single time we wanna work with this map, I'm just going to use a type def and make an alias. And we're gonna call this at map. Very, very simple. I don't like making type defs, but they do serve their purpose, and I believe this is one of those reasons. All right, so let's go ahead and make a function called getCats. And getCats is going to do exactly what you think it does. All right, notice what we're doing here. We are going to make a QMap, and we're going to return it. Now, this is the other thing that confuses people. QMap itself is not a QObject, so you can copy this all day long. But you cannot copy QObject, which we have in our values, which is why we are using a pointer. And we're using smart pointer, so we have automatic memory management. Okay, now that you've completely slammed all that into your brain, you can move forward. To say int, i equals zero. i is going to be less than five. Let's go ahead and increment this. Now what we need to do is create our value. So I'm going to say Q shared pointer. And again, you can use the standard library built-in smart pointers. If you want, I'm just showing you the cute way of doing it. And this is going to be a new hat. Now, notice right off the bat, we don't have a parent-child relationship, and that's why we're wrapping this inside of a Q shared pointer. Remember the parent-child relationship for QObjects, the parent would automatically destroy the children, blah, blah, blah. I did all that in a previous video. We also covered Q shared pointer in a previous video. If you skipped it, go back, watch it. But basically, when cat is no longer needed, Q shared pointer will automatically delete it. Makes memory management dead simple. Now, we're going to take that Q shared pointer and we're going to call set age, which is actually calling our cat, not the shared pointer. That's something that's really awesome about that. So now let's go ahead and set the age, and I'm going to use our Q random generator with the global instance, founded. We haven't really covered this class a whole lot, but basically what it's going to do is make a random number, not a cryptographically secure number, but a random number nonetheless between one and five. So we want our cat to be between the ages of one and five. Now let's go ahead and set the name.  I know, so sad. Now we need to take our value and insert it into the map. So I'm going to say cats dot. And this is where a lot of people type append and then they're shocked there's no append. This is an unordered list. So what we really need to do is say insert. And now we need a key, which is a Q string. So I'm going to say pet. And let's go ahead and expand this out. Let's say plus Q string number, which is going to take Q string and convert an integer, in this case, I into a Q string. So we're just adding those together. And then we're going to add our smart pointer in as the value. I know that seems a little confusing. So quick recap, that's plus a Q string representation of I is our key. So it's going to be like pet zero, pet one, pet two, pet three. As long as that key is unique, it should work. And then we have our Q shared pointer, which holds a pointer to the cat, which is our value. We're doing all this. So we have automatic memory management. So it seems super confusing at first, but once you get into it, it's actually pretty straightforward. Now let's go ahead and say cat map. This is where type def really shines because we don't have to retype all that. Let's call this cats equals get cats. And if we wanted to, we can just simply Q info this out, save run, and let's see what this looks like here. So there it is in all of its glory. You can see we have our cats are constructed and this is the big, ugly, nasty Q map in memory of what it looks like here. And you can see we have our Q shared pointers holding the pointers to the cat classes. All right, our last section really didn't do us any favors as far as displaying a Q map. Let's rerun that just so you can see what it looks like here. Horrible. I mean, you can't read this. So let's make a better function. Let's show how to actually display a Q map. There's multiple ways of doing it. I'm just going to show you one that's dead simple. And we're going to say cat map address of and then whatever we want. So the reason why we're going to use the address is so we're not making a copy of it. I want to show you directly what that thing is. From here, if you already know C++, this is extremely simple. So I'm going to say for each and I want the Q string key in the cats.keys. And this is just simply going to return a Q list of strings. So should say a Q list of what our data type R key is. Now that you have the key, you can simply get the data that's associated with it. So for example, I can say Q shared pointer cat and I want the cats.value. And notice it's wanting the key. This is what I mean by it's not a positional or an index base at array. It's a key value pair. We have to tell it what key associates with that value. You can spoiler alert, add in a default value. We're not going to do it for this example, but you could if you wanted to. So if you were going to say, hey, if that key's not found, I want to know what to do with it. Now from here, it's very, very simple. Just say Q info. Let's go ahead and output that key so we can see what that looks like. And then let's go ahead and output the name of the cat. Go ahead and put the age of the cat. And then let's actually see the pointer itself. So I'm going to say ptr.data, which is going to give us that pointer to the actual object the Q shared pointer is using. We grab this, scroll down, and then it just becomes extremely simple. And I'm going to comment this out just so it doesn't become super confusing and let's run it. Sure enough, it's just that simple. At zero, pet one, pet two, pet three, pet four, it doesn't necessarily have to be in order. These keys just have to be unique. There's the name, there's the age. We got our Q random number generator. And then here's the pointer to each and every individual cat value in there. All right, so far we have created cats and we've displayed cats. Let's look at modifying an item. So this is where people are going to get very confused very fast, but we're going to try and overly simplify this. So I'm going to say avoid modify cats. So what we're going to do is we're going to take our cat map. You want to make sure you're using the address of or a pointer to it. That way you're modifying the actual list, not a copy. And we want the strings. We need Q string, E. And when I say we want the string, really what we're looking for is that key in our case, the key is a Q string. So very important, you understand what we're doing. We're going to look this item up by its key and then modify it. So let's go ahead and say if cat, actually let's call this cats, like trip me up just a minute there. So let's call this cats.contains. And now we want to make sure we have the actual key in there. Then let's go ahead and say Q info, modifying and then whatever the actual key is. This is where people get very, very confused. So if you say cats. And you try at, there's simply no at there. It's just not there. And that's where people go, oh, I don't understand. You want to access it like this using the brackets and then give it the actual key. In this case, I'm going to say our key and then you can call it directly. So we're going to say set name. Notice what's happening here though. It's saying cats, whatever the key is dot and then we're accessing that variable directly. So we can say like set age, 99. I don't know if you caught it, but as soon as I said set age, this turned from a dot to an arrow. So the ID is smart enough to know that we're actually working with a pointer. We can just grab this to the magic of copying paste. And let's just do that again. So I'm going to say dot on the keyboard. And this is where people get confused. You see like clear, create data. These are all part of the Q share pointer, not the actual cat class. That's again, not a complexity with the Q map, but when you start working with pointers and smart pointers and everything, it's going to get super confusing. So we're going to do set name and notice it changed the arrow for me. And notice it changed the arrow for us. And I'm going to call this lucky. And then we're going to go ahead and call our display on cats. See, it's really not that super hard. So back down here, let's go ahead and say, modify cat and we want our cats map and we want to modify the key pet too. Let's go ahead and run this and see what this looks like. Okay, so pet two is unknown. We modify pet two and now pet two is fluffy with an age of 99. And we've modified that directly in our cat class. The major takeaway from this little segment here is that you're going to access it from a key, not an index position. Now that we can modify an item, let's look at how to add an item or inserting it. So I'm going to just say, please say void. Let's call this ad cat. Again, one of the common mistakes is that if you don't do an address of our pointer, you're actually making a copy of the Q map and then modifying the copy and then wondering why your original did not actually modify. Oh boy, that's really confusing. So you info, adding and inserting. This is a little bit confusing because what's the difference between adding and inserting? Well, there really isn't one. You're not going to say inserted at position because there's really no concept of a position here. So what we're going to do now is we're going to say Q shared pointer cat. And let's call this PTR. And we want this to be a new cat. Again, we're not going to use the concept of a parent child relationship because well, guess what? We want the Q share pointer to manage that memory for us. And then we can just simply say PTR dot set age. And let's make this like an insanely old cat, thousand years old. And then PTR dot set name. Let's call this test cat. Test cat got irradiated and it's now immortal and test cat will live on forever. Now there's a couple of different ways of doing this. So for example, you can say cats dot insert. And you notice that there's no position. This is where people get really confused. They see this cons iterator pose, but it doesn't really work the way you think it would. Remember, you have to have a key value pair. So we're going to insert this as test and then add the value. So that will definitely add it, but there's also another way you can modify it directly like this cats. And then let's go ahead and display. Now you may be wondering which is the correct way of doing this. Honestly, I almost always do this simply because it's very obvious what you're doing where if this already exists, notice we're not checking to see if it exists. It's just going to overwrite it. And that can get a little bit dangerous. Let's go ahead and demonstrate this. So I'm gonna say add cat cats. Say run, let's see what this looks like here. So sure enough, there is our test cat at the very end. It did add it. Now remember I said every key has to be unique. So let's go ahead and call add cats multiple times, but the major caveat being we're not checking to see if it already exists. So this is an underlying problem with our code. What just happened? Do you see it? Test cat got deconstructed, meaning what we did is we reused that key. And you can tell because right here, test cat has this memory address. You don't have to memorize the whole thing, but just look at these last letters, A C E 60. But now test cat is A D zero B zero. So this is a completely different instance. And the previous one got deconstructed simply because we reused the name. Okay, that's frustrating. Let's swap this out and go back to insert. You notice we still have the same problem here. Now, why do I prefer using insert? Because it's very obvious on the screen what I'm trying to do. And whenever I see this, I'm very conscious of the fact that I need to check to make sure that key actually exists. All right, so the last little segment at cat really showed us a problem. If we try to add this multiple times without checking for the existence of the cat, we're gonna have problems. So what we need to do is learn how to search for items. And there's multiple ways of doing this as you might suspect. So let me go ahead and grab this. We're gonna do the old copy and paste here. Let's call this find cat. Now I'm gonna show you a couple of different ways here. So first off, if you ever tried to find a cat in real life, you know, just how frustrating and funny it can be. There's an entire Facebook group out there to finding cats and it's just as messed up as you can possibly expect. So let's go ahead and say Q info. So in the spirit of that group, we're going to actually try to mess this up. We're gonna say exists. And then we're gonna say cats.contains. And we're just going to give it our key and just see if this actually exists. So let's go ahead and grab that girl down here. Let's do the old copy and paste save run. And let's see what this looks like. Uh-oh, I made a boo boo here, didn't I? I forgot to tell it what to look for here. Let's call test. Let's see if we can get an existence of test. Save run. Let's see what this looks like. So test cat exists. Let's modify that key to an uppercase T just to verify what happens. Notice how it is false. So this is actually case sensitive as terms of the key. That's one of the major takeaways here is if you're going to work with a Q string or a string as a key, make sure you watch your case sensitivity. All right, now that we know that exists, we can do different things with it. So for example, we can do something like auto, I did tell you we're going to screw this thing all up. You're probably going to see some hate mail in the comments down below. Cats.find. This is something I see people do all the time. And then they ask, well, what did find actually return? Well, it returned an iterator. So we're going to give it our key here. Now we need to determine if it's actually a valid value. This is the frustrating bit. So I'm going to say, if iter is not equal to cats.end, then I want to go ahead and say Q info. And let's do the iter dot key and the iter dot value. So let's play around with this and see what this looks like here. Let's say value stored in iter during its initializations never used, but if we did, it went away. That was just my idea trying to catch up. Save run, let's see what this looks like. All right, so there it is. Test Q shared pointer. So let's go ahead and change our key that we're looking for one more time. Do the uppercase t for test. We already know that will be false not found. So exist false. And then because we are testing, scroll up right here, we can avoid that altogether. So I generally don't work with fine just because it does get a little bit verbose and it gets a little buggy and error prone trying to figure it out. When I say buggy and error prone, usually the problem is with me, not the computer, but I just want you to be aware of that. Okay, so now let's go ahead and how would we get every single value? Save for each. And I want to do a Q shared pointer. Screwed that all up, didn't I? Let's say we're going to mess this up. So Q shared pointer, call this value. And we want the cats.values. So values is going to return a list of all the values with no key associated with it. So if you just want to go through every single value, you don't have to figure out what the key is. This is perfect for you right here. And then you can just simply do Q info, print that value out. Let's go ahead and run this, see what this looks like here. So exist false, it never did our iter and we can get all the values and find, oh, it's right there. But the major problem with this is of course it doesn't give us the key. Oh, that can be a bit frustrating, right? So let's just take this and change this. So we want Q string, which was our key data type. Let's call this key. And instead of values, we want keys. And this is going to do exactly what you think it would. It's going to go through all of the keys. And let's go ahead and say key, let's do the value. And this is where we have to say cats.value. And then we hand it a key. In this case, the key that is in our for loop right up here. And there is another thing we could do. We could do a constant or I should say a default return type as a const. I don't like to do this cause I like to make sure that I work with things that actually exist. But if you're working with ins and doubles and things like that, yes, this would be perfectly fine. But when you work with pointers, you want to make sure you actually have a pointer there. So I tend to avoid that. All right, so let's go ahead and do that. Save, run, and let's see what this looks like. Now remember we use the uppercase t for our key which didn't exist. So just iterating through or I should say looping through the keys, we can see the key value pairs and we can see the correct spelling of the key right there. Major takeaway from this is if you have no idea what you're looking for, you can just simply do a for loop or you can just go through a different way if you really wanted to. It makes it ridiculously simple to work with and it's nice and smooth. Let's look at how we would remove an item. So I'm going to just paste this and let's call this remove cat. We got our cat map and our key. Let's go ahead and check to make sure it's actually in there. So I'm going to say if not cats.contains our key, then we want to go ahead and basically tell the user, so sorry, try again. Say Q warning, in case you're wondering what Q warning is, it's part of Q debug, but it's just a different way of displaying a message. On the screen you're probably not going to notice a difference, but we're going to cover that in depth later on. In case you're really curious, it has to do with logging. We'd want different levels of notifications and logging. So we're just going to tell the user warning, the cat was not found, and then just hit the eject button and go right out of this thing. If we've gotten this far, well, it's actually very simple. So we're going to say cats.remove and then we just give it the key. And then let's go ahead and see what this looks like. I bet you were thinking it was super, super challenging. It's really not. So let's go ahead and remove our test and see what this does. All right, so bang, be constructed because we've removed that guy right here. And now our list is nice and neat. Doesn't have that test class in there at all. The major takeaway here is you work with pretty much everything through that key. It's very, I should say problematic or error prone. If you try working with the values directly, always use the key and then use the Q map built in functions to do any modifications you need to do.  So we got all this stuff constructed but where is it being destroyed? Well, this is why we've been working with a smart pointer. In this case, Q shared pointer. Again, you can use the built in standard library but this is a Q tutorial. So I'm going to use the Q shared pointer. It's very, very simple because we're using automatic pointers as soon as we remove an item or clear that list. Let's go ahead and demonstrate that. All of that memory would be freed up automatically. You just haven't been seeing it in the background. So let's run this and demonstrate. Ta-da. As soon as we remove it from the map, Q shared pointer comes in and goes, oh, it's no longer needed. The reference counts reduced to zero and it is deconstructed. Okay, so to summarize an overview, this is an example of how to work with the Q map and we're using pointers, specifically smart pointers and then working with the key value pairs and then following that object's life cycle. I hope you enjoyed this video. You can find the source code out on github.com. If you need additional help, myself and thousands of other developers are hanging out in the Voidromes Facebook group. This is a large group with lots of developers and we talk about everything technology related not just the technology that you just watched. And if you want official training, I do develop courses out on udemy.com. This is official classroom style training. If you go out there and the course you're looking for is just simply not there, drop me a note. I'm either working on it or I will actually develop it. I will put a link down below for all three of those and as always, help me help you. Smash that like and subscribe button. The more popular these videos become, the more I'll create and publish out on YouTube. Thank you for watching.


Hi everyone, good afternoon. So this session is going to be about app testing, continuous integration, and automation. Now, what I will be doing is I'll try and apply some of the concepts and principles of all these subjects that are used in software engineering as per se. And I'm trying to apply that in a mobile context. So before I start, I just want to know how many people have actually tried automating something in their app delivery process? Very few. So I'm hoping that there would be something in it for you guys to take back and try out within your delivery process itself. So to kind of set the context, we look at the agenda. So we look at the current state of play in terms of how app delivery is happening and what are some very major issues that are happening in the app development world. Then we look at some very common problems. I won't talk too much about these problems because I think most of you are familiar with these problems. We'll try and focus on some solutions that we have from our side using some open-source tools and processes that we've come across. And then we'll kind of talk about how we have actually used these tools. And finally, I'll try to wind up with a quick demo of time permits. So let's look at the current state of play. So any guesses on how many apps are there on the App Store as of today? Any guesses, a wild guess? Sorry? So it's about 1.4 billion, sorry, million. So that's a huge amount of apps. So one stunning statistic that I came across is that 60% of these apps on Play Store are getting downloaded. That's a huge number. 80% of the apps that are downloaded are not open more than once. So I was actually listening to some of the talks yesterday and somebody was talking about deep linking. I forgot the speaker's name, sorry, but I found that very interesting because it kind of addresses this second point a little bit because a lot of apps get downloaded. People never associate what to use the app with and it just remains around your phone until one day you decide, okay, I don't want this app anymore, I'll just uninstall. So that's not good, isn't it? So if you're looking at the overall figure, 92% of the apps are not getting used. Now to me that's a problem with app discovery. I hope all of you are familiar with this concept of app discovery. So it's basically you built an app and you want people to use it, but they should know that an app like this exists, right? So if you look at it, 60% of these apps are not even discovered by people. So imagine the amount of effort that has gone into creating them, but not probably enough effort to kind of market them and showcase them to the world. So that's a very important problem. The second one I want to look at is the quality of the apps. So this is something I've picked up from Play Store. This is a very popular app. I have not mentioned names here because I think these apps are wonderful ideas and they're doing pretty well, but unfortunately these are some of the comments that I picked from the Play Store. So it says, got another update today. It says bug fixed, but I can't see anything fixed. The same issue I reported a month back still exists, okay? And this was pretty interesting. Somebody has been very nice to the developer, they're saying it's fine, it's a nice app, but it doesn't work with 2G. Sometimes it doesn't work with 3G. And just for information, this app is very popular in India. They're kind of used to make payments. Now the whole purpose of having an app that allows you to make a payment is to allow somebody to make a payment on the go. Now if it doesn't work on 2G and is not stable on 3G, I wonder how people are going to use it. And I'm also hoping it works on Wi-Fi. So this is not a good state to be in, right? Now the problem here is, yes, the apps are buggy. Probably not enough testing has been done on them. And the fundamental problem to me is the problem of the delivery process. Because there is an objective that has been set for the app in the second case, which is probably not being addressed. And not enough people have actually looked at addressing them before the app was released. In the other case, the first one, somebody's actually reported a problem. Either the developer's not understood the problem that the user is facing, or he's fixed it and the fix has gone missing somewhere during the whole delivery cycle. So both of them to me are problem delivery process itself. So one final statistic is on crashes. So it says that 47% of the apps crash more than 1% of the time. And 2% of the apps crash 2% of the time. So that effectively means that every 100 times an app is opened, it crashes once. And the second state is like 200 out of 100. So is that what you want your app to be doing? So if you kind of look at it in reality, so I read these stats and I was wondering, it can't be so bad, right? So I'm a big cricket fan. I keep watching cricket following the matches that happen across the world. So I have an app that I use to check this course. So I read this and that there was a match going on and I kind of opened the app, checked the score, closed it, all fine. Tried it two, three times, all fine. Four times, the app just crashed. I was actually at a loss of words to explain this because nothing had changed. Same device, same network, same user. So why did the app crash? I still don't know. So then I thought maybe one out of four times and I opened it many, many times, it didn't crash. Probably until 20th or 30th time, it again crashed. So there is a problem that's hidden somewhere. We need to identify the problem. So if you look at an industry split of these crashes, it happens right across, right? Whether it is in gaming, multimedia, news updates, business apps, M-commerce apps. So crashes keep happening. The percentages may vary, but crashes keep happening. So this is where we are currently today. So there are problems with app discovery. There are problems with the quality of the app. Now app discovery is something you can address by having a tying up with people who are specialized in that area. You need funds for that. You need resources who can be engaged for that. And in terms of the quality of the app itself, I think you need to look at your internal processes and see where things are breaking. So there were two interesting thoughts. I think one was yesterday and one was today. Somebody, people were talking about how they started re-engineering the apps. In terms of the identified problems when the app, once the app went live and they wanted to make changes to address these issues. So I found them very interesting and I found them very, very much, I could relate to a lot of problems that they were talking about. And we'll try and address some of these, probably not a solution that all of you can use, but at least some of you can take back and try implementing. So let's look at why apps are buggy. So we would definitely believe that people test their apps, right?  Test your apps before it goes live. Okay, that's a bit scary. That's really scary. So what I'm going to talk about now is pretty much in line with the number of hands I saw. So the first statistic here is that researchers, so this is a researcher called Christina Knight. She is based out of the US. I don't know her. It is on the net. So she found that 77% of mobile users are worried about the performance and quality of their app before they buy it. It's natural, right? Notice the point in red. 51% of app developers say that they don't have time to properly test their apps before release. I think the numbers I saw here kind of agree with that. So if you look at the problem as such, let's just go to the app world for a minute and look at it from a reality perspective. Now imagine you're building a car. You use the best of engineers, you use the best of materials to build the car. And you now go and market it, you get a customer and you tell the customer, I've used the best of resources, I've used the best of engineering techniques to build this car. So I don't think that car is going to have a problem. But you know what? We spent so much time building it, we didn't have enough time to test it. So probably when you are going at 100 miles an hour, you might have a problem, the break may not work, but it's okay, we'll fix it later on. Now once you have a crash, we'll fix it. So it's a little exaggerated to compare a car with an app, I know, but the problem here is you're creating a product and your service, the experience of using a product is going to be what is going to sell your product, right? So that's a big problem for me. Now if you think this was a one-off, there's another survey I found out. So this was done by some of the top software companies in the world, like the likes of HP and Capgemini. So they found that only 31% of organizations actually test their mobile apps. Now that's again scary, because we're talking about software companies who are saying that we don't have time to test our apps. So that again is a problem. So it's kind of got us thinking on what is the problem? Why are people not testing apps efficiently? So we asked around people. We found a lot of reasons. I don't want to get into all of them because I think you're all familiar with most of these reasons. There were probably a thousand more reasons. I couldn't put it up here. So there's definitely a problem that we need to address. So let's start looking at them. So what we did is we spoke to a lot of people. So I started by speaking to people within my team, then within my organization, my friend's circle, I did a bit of research on the internet and I tried to look at what are these causes and then we grouped them. So what I'll do in the next few slides is I'll try to kind of highlight these groups of problems and some potential solutions to these group of problems. Problem one is people. Yeah, we are the main cause of issues, right? I'm sure most of you would agree with this statement because if you look at it, typically what are the problems that people cause? Poor designs or flows and poor code. People design the app, people write code. So this is the fundamental cause of issues, right? Any bugs in your app. So if you look at that from a broader perspective and try to understand why this happens, so typically when you get a developer on board, what do you expect him to do? Say write code, build an app, test the app. Simple, right? So why is it failing? What is the cause of the problem? The cause to me is that a lot of other activities that are involved in getting these up and running are not being addressed. To write code, you first need to understand the requirements, right? So I'll try to put that in perspective. So imagine I want to build another app that shows me Cricket Scores. Pardon me for my examples with Cricket, but so I have. So I want to build an app that gives me Cricket Scores and I call this developer and say, okay, dude, build this app that shows Cricket Scores. Simple requirement, right? Build an app that shows me Cricket Scores. I give you my APIs to hit to get a score. He says, okay, goes up, goes up, comes back to me after five or eight days with a wonderful app, which we've got a details score card and they tell him, no, no, no. This is not what I wanted to see the summary of the score. Now he's frustrated because of the requirement, right? But whose problem was it? Was it his or was it mine? To me, both of us had a problem. He didn't ask and I wasn't clear, right? So this is where problems start coming in. So you need to get a clear understanding of the requirement itself. So once you're done with that, you need to understand the design, the UI elements. Of course, you write the code. You need to follow coding guidelines and standards. Somebody has to review your code. We'll talk about the code review as well. You need to check what kind of resources are you using? Are you using the right APIs or not? You need to build, check in your code into a repository. I was actually speaking to somebody outside yesterday and I got a bit of a shock when they said, no, I asked him what repository he used and he said, yeah, we are planning to use it. So probably once our first version of the app is live, we'll use a repository. Strange. So once you check in the code, you build the app.  You have to have it right nowadays because you need to test it with a lot of people, with a lot of devices. We need to do regression testing. We need to understand code coverage, integration tests, bug fixes. So there are so many activities that got encapsulated into these three statements like write code, build the app and test it. So the problem here is that very often the developer is not allowed to give an estimate of time that he needs to build the app. Somebody does it for him and he has to cramp himself into that estimate. This is where issues start coming in, if all these other factors are not looked into. Now one potential solution to this problem is automate. You can automate a lot of these tasks, especially the ones in green. You can look at using the right tools or techniques to kind of automate these. So what I'll do in the next slide is try and take you to some of the techniques that we have used or come across. So the first one is, before I get into automation, I would like to talk a couple of minutes on understanding requirements itself. So because that's going to be the root cause of a lot of issues coming up. So if you look at that, we found something very interesting called behavior-driven development. So anyone heard about this? I can see a few hands going up and it's great. So the whole crux of the matter is that you start communicating more with all your stakeholders, whether it be a tester, your developer, your designer, the business guys or the manager, we need to talk a lot to get rid of misunderstandings or interpretations of a requirement. So if you typically look at what's happening in the software world, somebody gets this brilliant idea, a bulb flashes and says, I have this idea to build this app. And you start putting in requirements into a document and you mail it to your developer, go through it and start building the app. What happens? I have an idea. It's like a crickets code car app. I just talked about. I have this idea of an app. But how well have I captured it in the document is an issue. The quality of the document itself is an issue. I'm not saying we need to write a detailed document. I'm not one for that. What we need to do is talk. Just get people into a room and start talking, which is the essence of behavior-driven development. So it's based on test-driven development and domain-driven design. So I don't want to get into a lot of theories. It's a vast topic by itself. So I will not get into details. But the crux of it was communicate. And you use simple language, English, which is kind of universal, or something else that is understandable by everyone in the team. So another key factor is you break down your requirements into very, very small bits. You agree on an entry and exit criteria so that everyone has a common understanding. So if I take my example of the cricket app itself, what I would do is I'll say, my entry criteria is I'll give you an API that gives you the score card, the score details. And the exit criteria is you need to give me a NAP or a UI where I just see the summary of the score. That'll clear up, isn't it? Now you start communicating a lot more. You get more things done in a more efficient manner. Now, we came across this framework called JBehive, which kind of captures this whole essence of behavior-driven development. Now, if you go to the JBehive website, I've listed out the URLs here, so probably if the slide is published, you guys can go through it. The essence on the front page of JBehive website is this, write a story, map it to Java, configure your story, run the story, view reports. Sounds simple. Yes, it's a methodology. You have to adapt, try, implement. It'll fail. Try again. It'll succeed. So if you ask me my experience with behavior-driven development, what we did is probably about five, six months back, within my team, I had a requirement to build a very simple app, one or two screens. So I got my developer, I spoke to him about the requirements. He said he'll start. Then I called my designer, spoke to her with the requirements, so she started. And then finally when they came together, there's a lot of mismatch in their understanding, problems. So it's somewhere around that time when I came across this and I thought, okay, next time I should try this around. So I didn't get into a lot of details. I didn't take time to explain this to my developer or my designer. I just got them to my desk. We spent about 45 minutes, if I'm not wrong, discussing in detail about what to do. All three of us had a very clear understanding of what the other person is going to give them. And the earlier app, which is very similar in nature, took us around six, seven days to get the prototype out. This one took me two days. So there's definitely effort to be invested, but I think the return on investment is quite justifiable. Now, you might also turn around and ask me, how often is it possible to get everyone together and talk? At some point in time in your project, I'm sure everyone has to talk, right? Somebody, maybe your customer's sitting somewhere far away, US, UK, Europe, wherever, but you need to talk to them, right? So what you need to stand up and tell them is, boss, if you don't talk to me today, you are going to have to talk to me in a very bad language, probably six months down the lane, where everything is screwed up and you don't know what to do, I don't know what to do. So talk to me now, I'll ensure that I'll give you better quality. So I think that's a very important thing that you need to do in terms of re-looking at the way you deliver stuff. The next one I'm going to talk about, and I think a couple of guys mentioned about Jenkins in the slides, I think that kind of helps because continuous integration. How many of you are using this within your delivery process? Then very few hands. So this is something I think you should really, really adopt within your delivery process because have you all heard about continuous integration? Because I don't want to get into details. It's something that existed for ages. Anyone who's not heard about continuous integration? Okay, everyone knows. Okay, I saw a couple of hands there as well. So I won't get into details, but the whole philosophy of continuous integration is that you have to have a code repository. People check in code into a single place. Every time there's a check-in, you build it and ensure that it is stable so that a check-in does not affect anyone else. So that's, I don't know, two second introduction to continuous integration is such. So what it does is, it kind of eliminates issues from propagating. You catch issues earlier on and it doesn't just end with a build. You need to do some amount of validation on the build to ensure that it is stable, to ensure that it is robust and you can actually use it for your next levels. So there's a lot more to continuous integration. You can just Google, you'll get tons of material about this. The framework that we have adopted is Jenkins, which is very popular in this world. I think it existed for ages. I've used it in the past. So somebody asked me why Jenkins? Only reason is I just had experience with Jenkins and it works for me, so that's it. So the next problem I'll talk about is fragmentation. But before that, related to the previous slide, there were two more points which I would like to talk about in a little more detail. One is on test automation. Morning also a couple of guys were asking questions on test automation, cross-platform automation. I'll attempt to show you a framework that has helped us in some ways in that area. So I will not talk in too much detail about it at the moment, but what I would do is spend a minute on code review. How many of us actually do code review? Fantastic, but still 50%. So we're actually giving opportunity for issues to go through to the customer, right? If you're not reviewing code. So the other aspect is how do you do code review? Typically you have a coding standard or guideline that somebody has defined for you. It could be Google, it could be your organization or your lead, somebody has defined it. And what does it generally have? Your code has to be intended, five spaces on the left. You have to have a naming convention, start with lowercase, uppercase, blah, blah, blah, right? Is that what you want to do in code review? Or is that what you should be doing in code review? You just check it, she asks me, no. That's the very basic thing that any developer should do. What you should do in code review is actually understand the business requirement, map the code and see whether it is creating any issues with that business requirement. A, is it satisfying it? B, if it is satisfying, how well is the code written? Trust me, you can actually identify or catch a lot of performance issues if you do a effective code review. A lot of bugs are caught in effective code review. You should try and practice it and I think it's very important. So you can't automate that bit, definitely. But what you can automate is checking these guidelines and standards, which is what most people try to do in a code review activity. So how you can do it is using a lot of tools. There are quite a few out there, something called PMD, ChexStyle, if I've got the name of a couple of others, but there are quite a few out there which you can actually use within your delivery process. It's really gonna help you. So moving on to the next problem, which is fragmentation. I'm not going to get into it. All of you know what I'm talking about. But just want to throw or set a bit of context. There are two issues.  So that's kind of all the Android versions as of last week. They are split up across devices. And of course, the screen sizes and densities. We're all aware of this. But let me just shake you up with one more thing. This is something called from opensignal.com. So there are sites that gives you a lot of information about how handsets are behaving and a lot of statistics about handsets and OS usage. So if you look at it, 2003, November, there are around 11,800 devices that were available which are running Android across the world. 2014 November, that's shot up to more than 18,000 devices. So looking at an increase of about 6,000 plus devices in a year's time. Now that's just going to create more havoc in this fragmented world, right? And now you're looking at scenarios where you need to deploy an app on one version of the OS on one version of the hardware. Suddenly another version comes in. How do you handle this? So to me, that's a very big challenge. I think all the developers or testers or organizations are facing. Now what are the problems? Perceive problems are that you need to check all these factors, right? So there are quite a few listed here, probably a few more that I can add on, but there are quite a few problems that people perceive that they have. But to me, the actual problem is managing your devices. So when you say managing your device, it's like how do you decide on which device to buy? There are 6,000 plus devices that got added in last year. How many of them should I buy? How many of them can I ignore is a challenge? The next challenge is, okay, assume I buy 50 devices. How do I ensure that they're all charged? How do they ensure they're all kept up to date with the latest updates from the platform owners? How do I ensure that they're made available to the right people at the right time? How do I share these devices? So many things, right? You never think of this when you just start building your app. Suddenly you're facing with a lot of challenges that you've never thought of upfront. And the other important factor is cost. So you would agree with me that all these devices, they cost a lot, right? They're not cheap. So probably the high-end Android handsets are selling around 50K in rupees. And the low-end ones that you would really want to test are somewhere around 10, 15K range. So how many of them would you buy and how do you use them? So to me, these are the two important challenges. And one solution that I came about and I think this is a really innovative solution is mobile testing in the cloud. Anyone heard about this thing? Cool. Anyone tried using it? Unfortunately, I must confess I haven't used it, but anyone tried using it? Cool. So the whole concept here is that you have devices made available to you on the cloud and you interface with them using a web UI. So you might ask me, how do I install an app onto that and how do I check stuff? Well, they give you a lot of capabilities. I've just had a very minimal interaction with one of the vendors who are doing this. They come with a cost, definitely, but not as expensive as buying a device. So what you effectively do is, this is something like a platform as a service setup that they've done beautifully. So what they do is they take care of managing your devices. They give you an interface over the internet where you just log in. You say, I want XYZ device for so much duration. You buy the time slice and you start using them as if you own the device. Only thing is you can't physically catch the device in your hand. They give you a lot of video recordings about how the app is working, what is the memory usage, what is the CPU usage, how much file storage is used by your app. A lot of runtime dynamics are actually provided by these guys, which I think is very useful if you're an app developer and it kind of opens your mind to a lot of things you probably would not ever have thought of before. If you start using these capabilities. So to me, a potential solution for fragmentation is mobile testing on the cloud, which I think I would urge you guys to kind of go and look into a little more detail and see how you can adopt it. Because the beauty of it is you can actually contain your costs. You can actually say I have a budget of so much to be used for looking at devices that I can test. I think it's pretty good and very effective. And the beauty of it is any new device that gets added, they kind of add it on to the service. So you need not worry about buying a new device. The other aspect is app distribution. So distribution is nothing but the capability of... Imagine you have 10 testers, you want to send out your app to all of them and they start testing, one of them raises a defect and you don't want to fix it and give it to all 10 testers. You want to probably give it to the guy who's raised a defect. If it's been fixed, then you want to propagate it across all the 10 guys, right? So how do you tackle that? That itself is a challenge. Forget mobile-based testing, sorry. So forget that for a moment. Imagine you just have 10 devices that you want to use. How would you send these apps across to different people? So that's again a challenge, right? You need to first maintain or have a registry of who has which device, which device has what OS and which user has what version of your app. Then a bug comes, you fix it and you need to know, I need to send this updated version to this particular user. So how do you track it? Try using an Excel sheet for that. I think you'll give up in a week's time because it's very tough. The updates are so frequent. So that is our app distribution I think comes into picture and there are wonderful frameworks available at the moment. So one of them I've actually tried out is called Test Fairy. Anyone has heard about Test Fairy? Cool. So you guys, I'm hoping you've actually used it and I think it's very, very useful and the beauty of it is it's just free. They just give you so much of capability and it's free of cost. So you register with them, you give your app and they kind of take care of the distribution and the maintenance aspects of it which I think is just wonderful. It's a fantastic service they're giving. So that also allows you to take care of fragmentation to an extent because they're kind of managing your devices for you. Next thing is response times. So I think even today somebody from Hike was talking about flaky networks. How do you handle this? So they were talking about you need to build apps that cater to this. How do you handle this? Now that is a very big challenge, isn't it? So even from a comment from one of the earlier slides which said there was an app that was not working on 2G and sometimes unstable on 3G kind of thing. So you have services there which are relying on network speeds. How do you handle this challenge? So the diagram I have here is a statistic that I've managed to obtain again from the internet which kind of normalizes the network response times across the world. So the concept here is that if a response takes one second in the US, it takes 0.8 seconds in Canada and unfortunately 2.1 seconds in India. So our network is definitely not the best in the world but we do have a network. So problem here is you might be sitting here in India and you're trying to build an app that is probably to be used in Europe or US. And how do you ensure that this app is going to work there in the kind of network that they have? One potential solution is use simulators. Anyone has tried SOPI? Anyone has heard of SOPI? So it kind of allows you to mimic backend responses, right? It's a dummy thing but at least you can actually test the responses that you're going to get. SOPI also supports REST I think pretty recently so it's pretty good. If not build your own simulator, it doesn't take much. We've actually built one ourselves, a REST simulator and it just works beautifully. It allows you to do a lot of stuff. So it really opens up a lot of opportunity for you from an app testing perspective. Other thing you need to do is mimic the delays. So once you have this kind of an understanding, so testing is not just about open the app, click, click, click, click. No, not working and give it back to the developer. It's about thinking a lot, thinking ahead. And there you need to kind of factor in this kind of capability, mimic these delays. So if you're saying I'm going to set up two URLs, fire one with a delay of one second and another one with a delay of 2.1 seconds, you'll see how your app is behaving in India and probably in US. One more solution that I have is leveraging mobile cloud testing. So you might ask me how this works. So one of the capabilities which I found very useful on mobile cloud testing was the fact that they actually allow you to run your app on a network which is in the US or Europe. So they have type of mobile operators. You can actually run the app on a network that is live there. So you get a feel of how the app would respond were it being used by somebody in some other country. So I think that's again a solution. So we looked at three broad categories of problems. So I know I can't kind of address all of them together in one shot, but definitely there are solutions to all these problems. We need to invest time into solving these problems so that we can build something that's very good. So one last thing I think is planning. So we all talk about planning. We all have big plans for doing everything, whether it's going out for a movie or building an app. So we need to look at our plans. We need to ensure that we are addressing our problem, not somebody else's problem. We need to be very sure that we are facing the same problem that somebody else has before you try and solve that.  So when I say right flows, you pick up a lot of tools and typically why people discard them is the fact that they start looking at this tool is not working. It's too much of an overhead to kind of learn this tool. I think that's where people go wrong. They need to look at how these tools can effectively feed into each other, how they can help each other and how it can increase your overall productivity. And that really helps you a lot in building something that's really good. And tomorrow when you get feedback about your app, your entire next release cycle is kind of made much easier if you have all these tools with you. So when we looked at all these challenges, so I mean there were quite a few more, it's not that there are only three problems with the mobile app, there are probably zillion more things that you need to handle. But when we looked at all these, we looked at at least attacking these three or four very big issues that are plaguing people which we understood is not just within my team or my organization, it's across the industry. And I think I am justified in saying that we were right because yesterday and today's talks, people were talking a lot about these issues. So when we looked at identifying a solution, we looked at three important factors. One is simplicity. We didn't want to invest our time in learning something that was very difficult. People just get put off whether they already have enough of pressure to do stuff. So they try and want something that's simple or easy to adapt. We wanted to test what goes live. So I'm getting to a test automation suit here. So a lot of suits, a lot of stuff that we evaluated like something called monkey talk. Anyone heard of monkey talk? So a lot of you have probably used it as well. The challenge with monkey talk is the fact that you need to kind of inject additional code into your app for it to be running, which I think is a challenge. I don't want to do that if I'm building an app, right? I want to do something that is cross-platform. I think that's very important. We are a DroidCon, I know, but the unfortunate reality is that we need to build apps that probably work on iOS and Android, right? So I don't want to be rewriting test cases that are going to be running only on Android or on Windows. I want to do something that's cross-platform. And I think this is pretty interesting and something we felt would save a lot of time, scheduled and automated reporting, which is kind of nice to have things. Cost, cost is another deterrent. A lot of initiatives get lost. We wanted something at a simple open source and we didn't want to have a dependency on anyone tool when we built a framework. So when I say framework, get me wrong. We've not built something that's like going to change the world tomorrow, but we've just used things that are available there. We wanted to customize it and use it to solve problems. So we wanted to be built in such a way that tomorrow should that tool not exist. We don't have a dependency and a framework goes for a toss. So we've purposefully used a framework where you can just pull out stuff and put in something that you are comfortable with. And one last thing is you always need to buy in from your managers when you're doing something, right? Although they're just going to say, no, just write code. I don't care about automation. Just write code. That's not going to work. So you need to show them what is it that they will get by doing this. So we kind of put in some terminology around Peter on the bush and tell them that they would get X, Y, Z. But actually when we actually put in everything, we're actually able to show this. So this is from the way we have implemented this framework. And I've done these stats from probably across three or four months. So I've actually seen that there's been an reduction in the effort. And we were actually able to put together a pretty neat regression suit that is actually reducing my regression effort by around 70%. So these are apps. This is apps that we were using internally. And I know that these stats are right because I've been able to measure them hands-on. This would vary depending on how you're using these tools, how well you've adapted something in your environment. So before I get into a demo, one last statement about Appium. We used Appium as a test automation framework. Anyone has heard about Appium? Very few hands, okay? So that's something I think you can really look at adapting within your organization because they kind of gel well with the principles that we had in terms of being free. They're actually, they're a very strong open source and a very good community. They have releases once in two weeks, so I think that's pretty good. They kind of don't reinvent the wheel. They kind of use APIs that the frameworks are giving them and they try to use that instead of building something new. And it's quite easy. They give you the ability to program in a language that you are comfortable with. Java, Python, Ruby, there are quite a few there on the website for checking out. So what I'll do next is tell you how we have used them. We've used Git as our code repository. We've used Jenkins as a continuous integration tool. We use Appium to kind of distribute the apps onto the devices and we've drawn out some reports on Jenkins. Pretty simple, straightforward, but very effective for us. So we'll just jump to a quick demo. So what I'll be doing now is, this is my mobile screen. This is the Appium console and this is Jenkins. So what I will be doing now is I'm just mirroring my screen. And fortunately for some reason, my screen is not getting mirrored here. So I'll probably have to run it on my, then I wouldn't be able to show the screens as well. I want to show the other stuff. So it's a little, I think it's got something to do with the resolution. I'm sorry I wouldn't be able to show you that, but I can definitely run something on the screen and you can see this in action. So what I've done is we've got a simple action. So at least those of you sitting in front can see my device. And I can assure you that I'm not doing some kind of drama here. So what I've done is I've set up a simple job on Jenkins. I've got this app where I will just, I've written one simple test case, just for a demo purpose where I just log into the app and validate that I'm logging in. So what I'm doing is I just scheduled the Jenkins build. So there you go, the build is, so what it does now is it is actually connecting to Appium. You can see Appium is trying to make a connection. So Appium internally uses Node.js to kind of connect your device and send signals to it and run commands on it. So we just give it a couple of seconds. It's right now trying to connect to my device. Just a second I'll just, I think my device is not getting recognized for some reason. So while we wait for it, so what Appium actually does internally is, it is using ADB to talk to the device. You see here, it's actually run all the test cases fine, but for some reason it's not showing up on the app. So it's actually said that the test failed for some reason. The test was try and login with something so it actually gives you details about what has failed, error message, blah, blah, blah. So probably if I just take it off, it'll just start working. I can try one last time, but actually trying to run the, so it's basically running the test case right now. So it's trying to open up the app right now. So while the way we have done this is each time, it's still having some issue connecting my device. I'm not sure what the problem is. So sorry, probably once you guys can, I'll be outside, I can just show you this demo. I don't know for some reason it's just refusing to show up here. So that's pretty much what I wanted to talk to you guys about and I can assure you this thing works because I just tried it out here in the morning as well when they're testing it. So that about, I'm pretty much done what I wanted to cover. So before I take any questions, I just want to thank two people from my team who've really helped me set this up and get it going. So Pradeep and Anup, I know you guys are sitting somewhere here. Thanks a lot for your help to get this up and running. So I'm open to any questions that you guys may have. Sure. Actually, my main question is how you are doing inter-app communication by using Appium. Sorry? Inter-app communication. Inter-app communication. Now we haven't tried inter-app communication using Appium. So the whole purpose of using Appium is to kind of connect to your app. So it depends on how you want to try to test something. So you're trying to test the app. So if there is a functionality that requires communication between this and app one exposes a way of communicating, then you can definitely find. So the test case I've written is actually using JUnit. So if there is a way to call it from JUnit, we can just go ahead and use it. One more thing. Now you ran the build, but it failed. But still you're showing build successful there. OK. That's a configuration thing. If you actually go back here, I just kind of set this up on my laptop and I just said some stuff here. So you can actually change this message around to do something. I just said, OK, whatever happens, just show it as successful. You can actually go and change it. This is a Jenkins thing. It's got nothing to do with Appium. So I've just not configured my Jenkins properly. Hi. Yeah. Thank you to the really informative session. So I wanted to ask a few things by, let's say, taking an example. So let's say I'm writing an app which, for instance, counts data that has been run from the device. OK. OK. Which app, very simple app, which app is using how much data or things like that. OK. So what, so does this fit well? See, what I would want to do in such a scenario is that for Appium, at least, to go to my app, let's say, click on a button that says Start Counting. OK. Let's say I have a functionality Start and Stop. OK. And then after that, I want to maybe run a big download from some server, from some server on the web. OK, some 100 MB file that's to be downloaded. And then I do a stop counting. OK. OK. So can this be done using Appium first? OK. So first thing is, so you can do testing in two ways, right? So one way is, so the way we have adopted is, we didn't want to do a very intrusive testing. We wanted to get the APK file built from somewhere, and use it as it works. So we just wanted to go click, click, click as a user would do. The other way is, you have the code with you. So once you have code, you know the kind of APIs that the code exposes, right? So you could actually refer to that and write your test cases to do what you want. So yes, to answer your question, yes, it is possible. But you might need more details about how your app is working, what kind of interfaces the app exposes. If you are aware of it, yes, you can definitely do it. OK. So basically, I can integrate Appium into my app at build time? No, you can't. Appium is an external entity. You can't kind of use Appium. So what I was trying to say is your app is, if I understood your question, that your app connects to somewhere, downloads some stuff, you want to see how much data has been used. No, no, no. It just counts how much data any of the apps are using. So basically, I want to start counting. Then I want to, let's say, go into the Chrome browser. I have to open a link, which is a sample download from some file from the internet, which is, I know it's a 10 MB file. And after a successful download, I want to, or let's say, after 10 minutes, I want to come back and stop my app and see what is the counter that it has reported. So yes, so you use some kind of API that, say, the Android system would expose to get this data. You can call the same API using Appium. It'll do the job for you. But I basically want to do my call. I'm really sorry, but we'll just have to cut this short. He'll be available on the talk funnel app. So please go ahead and ask your questions there or take it offline to the speaker lounge. Thank you.


This win is because of him, I mean he came out and you know like records are kind of irrelevant like that's a very dangerous lineup. So for him to come out and you know pitch the way he did like that that's incredible and that's what he's capable of he's an unbelievable pitcher. So huge you know huge game for him huge game for us and you know he's definitely one of the driving reasons by why we won today. You had a big night offensively as well including your first career home run what allowed you to settle in at the plate tonight? You know it's just it's just you know at bats and you know it's kind of obviously you know sometimes when the playing time is sporadic you know that's my role and you know kind of have to embrace it but when you know you get multiple days worth of at bats you can kind of you know find your confidence find your groove and you know I think too though that one of the big keys for us today was just our lineup you know one through nine we really just you know pass the baton really well and guys you know up and down the lineup had phenomenal at bats today and you know that was like a total you know team like the definition of a team win. As far as the home run is concerned I have to imagine you've been dreaming of that moment for a while what were you thinking as you were rounding the bases? You know there wasn't much on my mind it was kind of a surreal experience you know I can't really think of like a better time you know then obviously one of the you know greatest rivalries in sports and be able to you know just have a big at bat for the team and you know in a big spot with guys on base and you know at the end of the day I'm just really excited that we won and you know kind of you know set the tone for the rest of the home stand. Mike thanks for the time congratulations. Thank you.


We continue with Chapter 15, Practicing the Holy Instant. This course is not beyond immediate learning unless you believe that what God wills takes time. And this means only that you would rather delay the recognition that His will is so. The Holy Instant is this instant and every instant. The one you want it to be, it is. The one you would not have it be is lost to you. You must decide when it is. Delay it not, for beyond the past and future, where you will not find it, it stands in shimmering readiness for your acceptance. Yet you cannot bring it into glad awareness while you do not want it, for it holds the whole release from littleness. Your practice must therefore rest upon your willingness to let all littleness go. The instant in which magnitude dawns upon you is but as far away as your desire for it. As long as you desire it not, and cherish littleness instead, by so much is it far from you. By so much as you want it, will you bring it nearer. Think not that you can find salvation in your own way and have it. Give over every plan you have made for your salvation in exchange for God's. His will contents you, and nothing else can bring you peace. For peace is of God and no one beside Him. Be humble before Him and yet great in Him. And value no plan of the ego before the plan of God. For you leave empty your place in His plan, which you must fill if you would join with me by your decision to join in any plan but His. I call you to fulfill your holy part in the plan that He has given to the world for its release from littleness. God would have His host abide in perfect freedom. Every allegiance to a plan of salvation apart from Him diminishes the value of His will for you in your own mind. And yet it is your mind that is the host to Him. Would you learn how perfect and immaculate is the holy altar on which your Father has placed Himself? This you will recognize in the holy instant in which you willingly and gladly give over every plan but His. For there lies peace perfectly clear because you have been willing to meet its conditions. You can claim the holy instant anytime, anywhere you want it. In your practice try to give over every plan you have accepted for finding magnitude in littleness. It is not there. Use the holy instant only to recognize that you alone cannot know where it is and can only deceive yourself. I stand within the holy instant as clear as you would have me. And the extent to which you learn to accept me is the measure of the time in which the holy instant will be yours. I call to you to make the holy instant yours at once for the release from littleness in the mind of the host of God depends on willingness and not on time. The reason this course is simple is that truth is simple. Complexity is of the ego and is nothing more than the ego's attempt to obscure the obvious. You could live forever in the holy instant beginning now and reaching to eternity but for a very simple reason. Do not obscure the simplicity of this reason for if you do it will be only because you prefer not to recognize it and not to let it go. The simple reason simply stated is this. The holy instant is a time in which you receive and give perfect communication. This means however that it is a time in which your mind is open both to receive and give. It is a recognition that all minds are in communication. It therefore seeks to change nothing but merely to accept everything. How can you do this when you would prefer to have private thoughts and keep them? The only way you could do that would be to deny the perfect communication that makes the holy instant what it is. You believe you can harbor thoughts you would not share and that salvation lies in keeping thoughts to yourself alone. For in private thoughts known only to yourself you think you find a way to keep what you would have alone and share what you would share.  Every thought you would keep hidden shuts communication off because you would have it so. It is impossible to recognize perfect communication while breaking communication holds value to you. Ask yourself honestly what I want to have perfect communication and am I wholly willing to let everything go that interferes with it forever. If the answer is no then the Holy Spirit's readiness to give it to you is not enough to make it yours for you are not ready to share it with him. And it cannot come into a mind that has decided to oppose it for the holy instant is given and received with equal willingness being the acceptance of the single will that governs all thought. The necessary condition for the holy instant does not require that you have no thoughts that are not pure but it does require that you have none that you would keep. Innocence is not of your making. It is giving you the instant you would have it. Atonement would not be if there were no need for it. You will not be able to accept perfect communication as long as you would hide it from yourself for what you would hide is hidden from you. In your practice then try only to be vigilant against deception and seek not to protect the thoughts you would keep to yourself.  Thus will he make you ready to acknowledge that you are host to God and hostage to no one and to nothing. And from the workbook, lesson 118 for morning and evening review, God's peace and joy are mine. Today I will accept God's peace and joy in glad exchange for all the substitutes that I have made for happiness and peace. Let me be still and listen to the truth. Let my own feeble voice be still and let me hear the mighty voice for truth itself assure me that I am God's perfect Son. On the hour, God's peace and joy are mine. On the half hour, let me be still and listen to the truth. Amen.


Hey everybody, Dr. O here. Let's talk about the para nasal sinuses. So what are they? They're hollow air filled spaces that are in certain bones of the skull. Their primary function is to lighten the skull but they also do play a role in residence which you know if you've ever, if you've ever been stuffed up it makes you sound a lot different. These sinuses are all covered or lined with nasal mucosa and at the mucosa production is increased and the sinuses get filled with fluid and that's going to change how you sound but also cause a lot of pressure and pain and discomfort. If you've ever had a sinus infection you know that's kind of a unique kind of pain. I've only had it one time and I certainly don't want to have any more. Alright let's go ahead and look at the sinuses so we'll start with their name for whatever skull bone they're actually in so the frontal sinus there at the top can either be the two like you see in this image or it can be one few sinus there. They're going to be the most anterior or the one that's closest to the front of your face. The largest sinus is the maxillary sinus. Sinus is also the one that is the most likely to be involved in sinus congestion and sinus infections these kind of things because of how they drain there since they're below the nose and the opening to where they drain is near the top. They're much harder to drain than the other sinuses so they can get filled up pretty easily. Next we have the sphenoid sinus so this is a single one. This is by far the most posterior of the sinuses. It's inside the sphenoid bone there and that's going to be the most posterior of your perinazle sinuses. Oh again, they're called perinazle sinuses because these are the ones that all connect to the nose just so you know. And then lastly we have the ethmoid air cells, these small little air sacs, these small spaces that are inside the ethmoid bone there. They're sitting right behind the superior nasal conche there in your nasal cavity. All right so those are your perinazle sinuses. Their functions are to lighten the bone and then play a role in sound production or resonance anyways and when there are problems they can get filled and lead to sinus congestion and sinus infections. I hope this helps. Until next time, be blessed.